From 9ffd154a2a5544de49cea1fcd3406b66f723a054 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 06:46:15 +0000 Subject: [PATCH 01/27] i18n: refine Slovak linked nodes and localize validation errors --- .github/scripts/run_offline_qualified.py | 3 + AppImage/components/monitor-switcher.tsx | 22 +-- AppImage/lib/monitor-link-errors.ts | 29 ++++ AppImage/messages/en/common.json | 13 +- AppImage/messages/sk/common.json | 35 +++-- .../scripts/flask_monitor_links_routes.py | 19 ++- AppImage/scripts/monitor_links.py | 27 ++-- .../scripts/tests/test_monitor_links_i18n.py | 138 ++++++++++++++++++ tests/test_monitor_links_i18n.cjs | 133 +++++++++++++++++ 9 files changed, 379 insertions(+), 40 deletions(-) create mode 100644 AppImage/lib/monitor-link-errors.ts create mode 100644 AppImage/scripts/tests/test_monitor_links_i18n.py create mode 100644 tests/test_monitor_links_i18n.cjs diff --git a/.github/scripts/run_offline_qualified.py b/.github/scripts/run_offline_qualified.py index 8a9548cd..4ee4eb95 100644 --- a/.github/scripts/run_offline_qualified.py +++ b/.github/scripts/run_offline_qualified.py @@ -13,6 +13,8 @@ import sys import tempfile PYTHON_FILES = ( + "AppImage/scripts/tests/test_monitor_links_i18n.py", + "AppImage/scripts/tests/test_monitor_links.py", "AppImage/scripts/tests/test_local_app_icon_isolation.py", "AppImage/scripts/tests/test_local_app_icons.py", "AppImage/scripts/tests/test_docker_app_discovery.py", @@ -28,6 +30,7 @@ PYTHON_FILES = ( "AppImage/scripts/tests/test_health_persistence_recurrence.py", ) NODE_FILES = ( + "tests/test_monitor_links_i18n.cjs", "tests/lxc_updates/test_local_app_icons_ui.cjs", "tests/lxc_updates/test_docker_app_discovery_ui.cjs", "tests/lxc_updates/test_native_app_discovery_ui.cjs", diff --git a/AppImage/components/monitor-switcher.tsx b/AppImage/components/monitor-switcher.tsx index 89f8d249..7fb88eb9 100644 --- a/AppImage/components/monitor-switcher.tsx +++ b/AppImage/components/monitor-switcher.tsx @@ -14,6 +14,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "./ui/dropdown-menu" import { fetchApi } from "../lib/api-config" +import { monitorLinkError } from "../lib/monitor-link-errors" import { useT } from "../lib/i18n/provider" interface MonitorLink { @@ -185,8 +186,9 @@ export function MonitorSwitcher({ nodeName, compact = false }: { nodeName: strin await refreshCache("/api/monitor-links") setOfferOrigin(false) } catch (err) { - await refreshCache("/api/monitor-links") - setAddError(err instanceof Error ? err.message : t("monitorLinks.saveFailed")) + setAddError(monitorLinkError(err, t)) + try { await refreshCache("/api/monitor-links") } + catch { /* SWR shows the load error; keep the localized mutation error. */ } } finally { setAdding(false) } } const notNow = () => { @@ -260,7 +262,7 @@ export function MonitorSwitcher({ nodeName, compact = false }: { nodeName: strin {manageOpen && } {origin && { if (!open) notNow() }}> - event.stopPropagation()}> + event.stopPropagation()}> {t("monitorLinks.knownTitle")} {t("monitorLinks.knownBody", { name: origin.name })} @@ -290,7 +292,7 @@ export function MonitorSwitcher({ nodeName, compact = false }: { nodeName: strin function ManageLinksDialog({ open, onOpenChange }: { open: boolean; onOpenChange: (open: boolean) => void }) { const t = useT() - const { data, mutate } = useSWR("/api/monitor-links?all=1", fetcher, { revalidateOnFocus: false }) + const { data, mutate, error: loadError, isLoading } = useSWR("/api/monitor-links?all=1", fetcher, { revalidateOnFocus: false }) const [form, setForm] = useState({ name: "", url: "", product: "pve" }) const [addresses, setAddresses] = useState>({}) const [editing, setEditing] = useState(null) @@ -304,14 +306,14 @@ function ManageLinksDialog({ open, onOpenChange }: { open: boolean; onOpenChange await mutate() await refreshCache("/api/monitor-links") } catch (err) { - setError(err instanceof Error ? err.message : t("monitorLinks.saveFailed")) + setError(monitorLinkError(err, t)) } finally { setBusy(false) } } const send = (path: string, method: string, body?: unknown) => fetchApi(path, { method, ...(body === undefined ? {} : { body: JSON.stringify(body) }) }) return - event.stopPropagation()}> {t("monitorLinks.dialogTitle")} @@ -320,7 +322,7 @@ function ManageLinksDialog({ open, onOpenChange }: { open: boolean; onOpenChange

{t("monitorLinks.addTitle")}

-
+
setForm({ ...form, name: event.target.value })} /> @@ -350,6 +352,8 @@ function ManageLinksDialog({ open, onOpenChange }: { open: boolean; onOpenChange {error &&

{error}

}
+ {isLoading &&

{t("monitorLinks.checking")}

} + {loadError &&

{t("monitorLinks.loadFailed")}

} {data?.links.length === 0 &&

{t("monitorLinks.empty")}

} {data?.links.map(link => { const own = link.manual || link.custom_url @@ -378,8 +382,8 @@ function ManageLinksDialog({ open, onOpenChange }: { open: boolean; onOpenChange {t("monitorLinks.thisNode")}
: isEditing - ?
- + setAddresses({ ...addresses, [link.key]: event.target.value })} />
-

{store.error || t("pbs.capacityUnavailable")}

+
} const tone = usageTone(percent) @@ -62,10 +64,11 @@ export function DatastoreUsageRow({ store, compact = false }: { store: PBSDatast // Same layout as the "Proxmox storage" cards of the PVE Monitor. export function PBSDatastoreCard({ store }: { store: PBSDatastore }) { const t = useT() + const { language } = useI18n() const percent = datastorePercent(store) const tone = percent == null ? null : usageTone(percent) const fullDate = store["estimated-full-date"] - const type = store["backend-type"] || "datastore" + const type = store["backend-type"] || t("pbs.datastoreType") return
@@ -99,13 +102,13 @@ export function PBSDatastoreCard({ store }: { store: PBSDatastore }) {
) : ( -

{store.error || t("storage.capacityNotReported")}

+ )} {(store.path || fullDate !== undefined) &&
{store.path && {store.path}} {fullDate !== undefined && {t("pbs.estimatedFull")}: - {fullDate > 0 ? new Date(fullDate * 1000).toLocaleDateString() : t("pbs.estimatedFullUnknown")} + {fullDate > 0 ? new Date(fullDate * 1000).toLocaleDateString(language) : t("pbs.estimatedFullUnknown")} }
}
diff --git a/AppImage/components/pbs-panel.tsx b/AppImage/components/pbs-panel.tsx index 5ea6856d..5dff026e 100644 --- a/AppImage/components/pbs-panel.tsx +++ b/AppImage/components/pbs-panel.tsx @@ -14,8 +14,10 @@ import { Label } from "./ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog" import { PBSDatastoreCard, type PBSDatastore } from "./pbs-datastore-usage" +import { PBSProblemMessage } from "./pbs-problem-message" +import { pbsDiagnostic, pbsResultText, pbsServiceState, pbsTaskType, formatPBSDuration } from "../lib/pbs-messages" import { fetchApi } from "../lib/api-config" -import { useT } from "../lib/i18n/provider" +import { useT, useI18n } from "../lib/i18n/provider" interface Connection { id: string; name: string; url: string; token_id: string; fingerprint: string; has_token: boolean } interface Detected { name: string; url: string; fingerprint: string } @@ -26,6 +28,7 @@ interface PBSStatus { name: string; version: string | null; fetched_at: number; services: Record; datastores: PBSDatastore[] | null; tasks: Task[] | null; sync: Job[] | null; verify: Job[] | null; prune: Job[] | null; errors: Record; + error_details?: Record }>; } const emptyForm = { id: "", name: "", url: "", token_id: "", fingerprint: "", token_secret: "", username: "", password: "" } @@ -46,9 +49,10 @@ const TASK_ICONS: Record = { termproxy: Terminal, dircreate: Database, "create-datastore": Database, } -// PBS reports "OK", "WARNINGS: n" or the error text; a task without one is still running. +// PBS reports "unknown", "OK", "WARNINGS: n" or an error; no state is still running. function taskState(task: Task): keyof typeof badgeTone { if (!task.status) return task.endtime ? "muted" : "running" + if (task.status === "unknown") return "muted" if (task.status === "OK") return "ok" if (task.status.startsWith("WARNINGS")) return "warn" return "error" @@ -56,27 +60,19 @@ function taskState(task: Task): keyof typeof badgeTone { // PBS reports "OK", "WARNINGS: n" or the error text of the last run. function jobState(state?: string): keyof typeof badgeTone { - if (!state) return "muted" + if (!state || state === "unknown") return "muted" if (state.toLowerCase() === "ok") return "ok" if (state.toLowerCase().startsWith("warn")) return "warn" return "error" } -function formatDuration(seconds: number) { - if (!Number.isFinite(seconds) || seconds < 0) return "—" - if (seconds < 60) return `${Math.round(seconds)}s` - const minutes = Math.floor(seconds / 60) - if (minutes < 60) return `${minutes}m ${Math.round(seconds % 60)}s` - const hours = Math.floor(minutes / 60) - return hours < 24 ? `${hours}h ${minutes % 60}m` : `${Math.floor(hours / 24)}d ${hours % 24}h` -} - // `localOnly` is the PBS host's own Monitor: no connection picker, the // header Refresh already reloads the page data. Its tabs show one `view` each. export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: boolean; view?: "all" | "datastores" | "tasks" } = {}) { const showStores = view !== "tasks" const showTasks = view !== "datastores" const t = useT() + const { language } = useI18n() const { data: connections, error: connectionError, mutate: reloadConnections } = useSWR( "/api/pbs/servers", url => fetchApi(url), { revalidateOnFocus: false }) const [selected, setSelected] = useState("") @@ -85,7 +81,8 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool const [method, setMethod] = useState("password") const [busy, setBusy] = useState(false) const [message, setMessage] = useState("") - const [error, setError] = useState("") + const [error, setError] = useState(null) + const [errorFallback, setErrorFallback] = useState("pbs.saveFailed") const { data, error: statusError, isLoading, mutate } = useSWR( selected ? `/api/pbs/status?server=${encodeURIComponent(selected)}` : null, url => fetchApi(url), { refreshInterval: 30000, revalidateOnFocus: false }) @@ -102,7 +99,9 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool } const save = async () => { + setErrorFallback("pbs.saveFailed") setBusy(true); setError(""); setMessage("") + let saved = false try { // A password is sent once to create a read-only token; only the token is kept. const { username, password, ...connection } = form @@ -113,22 +112,26 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool : await fetchApi<{ id: string }>("/api/pbs/servers", { method: "POST", body: JSON.stringify({ ...connection, id: form.id || undefined, token_secret: form.token_secret || undefined, }) }) + saved = true await reloadConnections() setSelected(result.id) setForm(emptyForm) setManage(false) - } catch (err) { setError(err instanceof Error ? err.message : t("pbs.saveFailed")) } + } catch (err) { setError(saved ? { body: { code: "pbs_saved_refresh_failed" }, message: pbsDiagnostic(err) } : err) } finally { setBusy(false) } } const remove = async () => { if (!form.id || !window.confirm(t("pbs.removeConfirm"))) return + setErrorFallback("pbs.removeFailed") setBusy(true); setError(""); setMessage("") + let removed = false try { await fetchApi(`/api/pbs/servers/${encodeURIComponent(form.id)}`, { method: "DELETE" }) + removed = true setForm(emptyForm) await reloadConnections() - } catch (err) { setError(err instanceof Error ? err.message : t("pbs.saveFailed")) } + } catch (err) { setError(removed ? { body: { code: "pbs_removed_refresh_failed" }, message: pbsDiagnostic(err) } : err) } finally { setBusy(false) } } const now = Date.now() / 1000 @@ -150,8 +153,9 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool {t("pbs.manage")}
} - {(connectionError || statusError) &&

- {String((connectionError || statusError)?.message || t("pbs.loadFailed"))}

} + {(connectionError || statusError) &&
+ +
} {isLoading &&

{t("pbs.loading")}

} {!selected && !connectionError && connections &&

{t("pbs.noServer")}

} {!localOnly && !manage && (connections?.detected?.length || 0) > 0 && @@ -171,7 +175,7 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool } {!localOnly && { if (!busy) setManage(open) }}> - + {t("pbs.manage")} {method === "password" && !form.id ? t("pbs.passwordHelp") : t("pbs.permissionsHelp")} @@ -201,7 +205,7 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool )}
} -
+
{(["name", "url", ...(method === "password" && !form.id ? ["username", "password"] : ["token_id", "token_secret"]), "fingerprint"] as const).map(field => { const secret = field === "token_secret" || field === "password" return
@@ -213,7 +217,9 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool })}

{t("pbs.tlsHelp")}

- {error &&

{error}

} + {!!error &&
+ +
} {message &&

{message}

}
{form.id &&
} {data && <> {!localOnly &&
- {data.name} · PBS {data.version || "—"} · {t("pbs.updatedAt", { time: new Date(data.fetched_at * 1000).toLocaleTimeString() })} + {data.name} · PBS {data.version || "—"} · {t("pbs.updatedAt", { time: new Date(data.fetched_at * 1000).toLocaleTimeString(language) })} {Object.keys(data.services).length > 0 && Object.values(data.services).every(state => state === "active") ? {t("pbs.servicesActive")} : Object.entries(data.services).filter(([, state]) => state !== "active").map(([service, state]) => - {service}: {state})} + {service}: {pbsServiceState(state, t)})}
} {Object.entries(data.errors).filter(([section]) => section === "datastores" ? showStores : showTasks) - .map(([section, detail]) =>

{t(`pbs.${section}`)}: {detail}

)} + .map(([section, detail]) =>
+

{t(`pbs.${["datastores", "tasks", "sync", "verify", "prune"].includes(section) ? section : "title"}`)}

+ +
)} {showStores && {t("pbs.datastores")} @@ -268,14 +277,14 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool return - {row.worker_type} + {pbsTaskType(row.worker_type, t)} {row.worker_id && {row.worker_id}} - {row.starttime ? new Date(row.starttime * 1000).toLocaleString() : "—"} - {row.starttime && end ? formatDuration(end - row.starttime) : "—"} - - {state === "running" ? t("pbs.running") : row.status || "—"} - + {row.starttime ? new Date(row.starttime * 1000).toLocaleString(language) : "—"} + {row.starttime && end ? formatPBSDuration(end - row.starttime, t) : "—"} + + + })} } @@ -299,7 +308,7 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool {t(`pbs.${kind}`)} · {row.id} {row.store || "—"} {row.schedule || "—"} - {row["last-run-state"] || "—"} + )} })()} @@ -309,3 +318,15 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool } } + +function PBSResultBadge({ status, running = false, tone }: { status?: string; running?: boolean; tone: string }) { + const t = useT() + const badge = + {running ? t("pbs.running") : pbsResultText(status, t)} + + if (running || !status || /^ok$/i.test(status)) return badge + return
+ {badge} +
{status}
+
+} diff --git a/AppImage/components/pbs-problem-message.tsx b/AppImage/components/pbs-problem-message.tsx new file mode 100644 index 00000000..459701c4 --- /dev/null +++ b/AppImage/components/pbs-problem-message.tsx @@ -0,0 +1,18 @@ +"use client" + +import { pbsErrorText } from "../lib/pbs-messages" +import { useT } from "../lib/i18n/provider" + +// Keep PBS's original diagnostic available without making it the UI message. +export function PBSProblemMessage({ problem, raw, fallback, className = "" }: { + problem?: unknown; raw?: string; fallback?: string; className?: string +}) { + const t = useT() + return
+

{pbsErrorText(problem, t, fallback)}

+ {raw &&
+ {t("pbs.technicalDetails")} +
{raw}
+
} +
+} diff --git a/AppImage/components/system-overview.tsx b/AppImage/components/system-overview.tsx index ecbeeef7..7458485b 100644 --- a/AppImage/components/system-overview.tsx +++ b/AppImage/components/system-overview.tsx @@ -7,6 +7,8 @@ import { Progress } from "./ui/progress" import { Badge } from "./ui/badge" import { Cpu, MemoryStick, Thermometer, Server, Zap, AlertCircle, HardDrive, Network, ChevronRight, Database } from "lucide-react" import { DatastoreUsageRow, type PBSDatastore } from "./pbs-datastore-usage" +import { PBSProblemMessage } from "./pbs-problem-message" +import { pbsDiagnostic } from "../lib/pbs-messages" import { NodeMetricsCharts } from "./node-metrics-charts" import { NetworkTrafficChart } from "./network-traffic-chart" import { TemperatureDetailModal } from "./temperature-detail-modal" @@ -187,14 +189,20 @@ const getUnitsSettings = (): "Bytes" | "Bits" => { return raw && raw.toLowerCase() === "bits" ? "Bits" : "Bytes" } +interface PBSOverviewStatus { + datastores: PBSDatastore[] | null + errors?: Record + error_details?: Record +} + export function SystemOverview({ product = "pve" }: { product?: "pve" | "pbs" } = {}) { const t = useT() const { language } = useI18n() const isPbs = product === "pbs" // On Proxmox Backup Server the datastores take the place of guests and PVE storage. - const { data: pbsStatus, error: pbsError } = useSWR<{ datastores: PBSDatastore[] | null }>( + const { data: pbsStatus, error: pbsError } = useSWR( isPbs ? "/api/pbs/status?server=local" : null, - (url: string) => fetchApi<{ datastores: PBSDatastore[] | null }>(url), + (url: string) => fetchApi(url), { refreshInterval: 60000, revalidateOnFocus: false }, ) const datastores = pbsStatus?.datastores ?? null @@ -545,13 +553,15 @@ export function SystemOverview({ product = "pve" }: { product?: "pve" | "pbs" } - {!datastores && !pbsError ? ( + {!pbsStatus && !pbsError ? (
) : !datastores ? ( -

{t("pbs.loadFailed")}

+
+ +
) : datastores.length === 0 ? (

{t("pbs.noDatastores")}

) : ( @@ -762,8 +772,12 @@ export function SystemOverview({ product = "pve" }: { product?: "pve" | "pbs" } {isPbs ? (
{t("pbs.datastores")}
- {!datastores ? ( -
{pbsError ? t("pbs.loadFailed") : t("pbs.loading")}
+ {!pbsStatus && !pbsError ? ( +
{t("pbs.loading")}
+ ) : !datastores ? ( +
+ +
) : datastores.length === 0 ? (
{t("pbs.noDatastores")}
) : datastores.map(store => )} diff --git a/AppImage/lib/pbs-messages.ts b/AppImage/lib/pbs-messages.ts new file mode 100644 index 00000000..6ce6faa3 --- /dev/null +++ b/AppImage/lib/pbs-messages.ts @@ -0,0 +1,77 @@ +type Translate = (key: string, params?: Record) => string + +const ERROR_KEYS: Record = { + pbs_config_read: "pbs.errors.configRead", pbs_invalid_config: "pbs.errors.invalidConfig", + pbs_invalid_field: "pbs.errors.invalidField", pbs_invalid_url: "pbs.errors.invalidUrl", + pbs_name_required: "pbs.errors.nameRequired", pbs_token_id_format: "pbs.errors.tokenIdFormat", + pbs_token_secret: "pbs.errors.tokenSecret", pbs_invalid_token_id: "pbs.errors.invalidTokenId", + pbs_invalid_fingerprint: "pbs.errors.invalidFingerprint", pbs_fingerprint_mismatch: "pbs.errors.fingerprintMismatch", + pbs_token_rejected: "pbs.errors.tokenRejected", pbs_invalid_response: "pbs.errors.invalidResponse", + pbs_invalid_json: "pbs.errors.invalidJson", pbs_connect_failed: "pbs.errors.connectFailed", + pbs_invalid_id: "pbs.errors.invalidId", pbs_invalid_datastores: "pbs.errors.invalidDatastores", + pbs_username: "pbs.errors.username", pbs_password: "pbs.errors.password", + pbs_login_rejected: "pbs.errors.loginRejected", pbs_invalid_login: "pbs.errors.invalidLogin", + pbs_token_create_denied: "pbs.errors.tokenCreateDenied", pbs_invalid_token: "pbs.errors.invalidToken", + pbs_audit_role_denied: "pbs.errors.auditRoleDenied", pbs_not_found: "pbs.errors.notFound", + pbs_read_failed: "pbs.errors.readFailed", pbs_invalid_list: "pbs.errors.invalidList", + pbs_local_unavailable: "pbs.errors.localUnavailable", pbs_auth_required: "pbs.errors.authRequired", + pbs_save_failed: "pbs.saveFailed", pbs_remove_failed: "pbs.removeFailed", + pbs_saved_refresh_failed: "pbs.savedRefreshFailed", pbs_removed_refresh_failed: "pbs.removedRefreshFailed", + pbs_datastore_offline: "pbs.errors.datastoreOffline", pbs_datastore_unavailable: "pbs.errors.datastoreUnavailable", +} + +// Accept structured codes from fetchApi, partial status or a datastore. Legacy +// English errors remain diagnostics; never interpret arbitrary text as a key. +export function pbsErrorText(error: unknown, t: Translate, fallback = "pbs.loadFailed"): string { + const body = error && typeof error === "object" && "body" in error ? error.body : error + if (!body || typeof body !== "object" || !("code" in body) || typeof body.code !== "string") return t(fallback) + if (body.code === "pbs_connection_limit") { + const params = "params" in body ? body.params : null + const count = params && typeof params === "object" && "count" in params ? params.count : null + return typeof count === "number" && Number.isSafeInteger(count) && count > 0 + ? t("pbs.errors.connectionLimit", { count }) : t(fallback) + } + return Object.prototype.hasOwnProperty.call(ERROR_KEYS, body.code) ? t(ERROR_KEYS[body.code]) : t(fallback) +} + +export function pbsDiagnostic(error: unknown): string | undefined { + if (!error || typeof error !== "object") return undefined + const body = "body" in error ? error.body : null + if (body && typeof body === "object" && "error" in body && typeof body.error === "string") return body.error + return "message" in error && typeof error.message === "string" ? error.message : undefined +} + +const TASK_TYPES: Record = { + backup: "backup", reader: "restore", verify: "verify", verificationjob: "verify", + verify_group: "verifyGroup", verify_snapshot: "verifySnapshot", prune: "prune", prunejob: "prune", + garbage_collection: "garbageCollection", sync: "sync", syncjob: "sync", aptupdate: "updateCheck", + termproxy: "terminal", dircreate: "createDatastore", "create-datastore": "createDatastore", +} +export function pbsTaskType(type: string | undefined, t: Translate): string { + return type && Object.prototype.hasOwnProperty.call(TASK_TYPES, type) + ? t(`pbs.taskTypes.${TASK_TYPES[type]}`) : t("pbs.taskTypes.other", { type: type || "—" }) +} + +export function pbsResultText(status: string | undefined, t: Translate): string { + if (!status || status === "unknown") return t("pbs.states.unknown") + if (status.toLowerCase() === "ok") return t("pbs.states.ok") + const warnings = /^WARNINGS:\s*(\d+)\s*$/i.exec(status) + if (warnings && Number.isSafeInteger(Number(warnings[1]))) return t("pbs.states.warnings", { count: Number(warnings[1]) }) + return /^warn/i.test(status) ? t("pbs.states.warning") : t("pbs.states.error") +} + +export function pbsServiceState(state: string, t: Translate): string { + const known = ["active", "inactive", "failed", "activating", "deactivating", "unknown"] + return t(`pbs.serviceStates.${known.includes(state) ? state : "unknown"}`) +} + +export function formatPBSDuration(seconds: number, t: Translate): string { + if (!Number.isFinite(seconds) || seconds < 0) return "—" + const unit = (key: string, count: number) => t(`pbs.durationUnits.${key}`, { count }) + if (seconds < 60) return unit("seconds", Math.round(seconds)) + const minutes = Math.floor(seconds / 60) + if (minutes < 60) return `${unit("minutes", minutes)} ${unit("seconds", Math.round(seconds % 60))}` + const hours = Math.floor(minutes / 60) + return hours < 24 ? `${unit("hours", hours)} ${unit("minutes", minutes % 60)}` + : `${unit("days", Math.floor(hours / 24))} ${unit("hours", hours % 24)}` +} diff --git a/AppImage/messages/en/common.json b/AppImage/messages/en/common.json index aa4a5c4a..f4a3b71c 100644 --- a/AppImage/messages/en/common.json +++ b/AppImage/messages/en/common.json @@ -72,7 +72,55 @@ "estimatedFull": "Estimated full", "estimatedFullUnknown": "Not enough data yet", "duration": "Duration", - "noJobs": "No sync, verification or prune schedules are configured." + "noJobs": "No sync, verification or prune schedules are configured.", + "removeFailed": "Could not remove the PBS connection.", + "savedRefreshFailed": "The PBS connection was saved, but the connection list could not be refreshed. Reload the page before trying another change.", + "removedRefreshFailed": "The PBS connection was removed, but the connection list could not be refreshed. Reload the page.", + "technicalDetails": "Technical details", + "datastoreType": "Datastore", + "errors": { + "configRead": "Could not read the saved PBS connections. Check the configuration file and permissions.", + "invalidConfig": "The PBS connection settings are not valid. Check the form and try again.", + "invalidField": "A connection field is not valid or is too long.", + "invalidUrl": "Use an HTTPS PBS address without embedded credentials, query parameters or a fragment.", + "nameRequired": "Enter a server name of at most 128 characters.", + "tokenIdFormat": "Enter the PBS API token ID in user@realm!token format.", + "tokenSecret": "Enter a valid API token secret without spaces or control characters.", + "invalidTokenId": "The API token ID must contain only ASCII characters.", + "invalidFingerprint": "Enter the certificate's SHA256 fingerprint: 64 hexadecimal characters, optionally separated by colons.", + "fingerprintMismatch": "The PBS certificate does not match the fingerprint. No credentials were sent.", + "tokenRejected": "PBS rejected the API token or its permissions. Check the ACLs of both the user and the token.", + "invalidResponse": "PBS returned an unexpected response. Redirects are not followed.", + "invalidJson": "PBS returned an invalid JSON document.", + "connectFailed": "Could not connect to PBS over verified HTTPS. Check the address, certificate and service.", + "invalidId": "The PBS connection ID is not valid. Reload the connection list.", + "invalidDatastores": "PBS returned an invalid datastore list.", + "connectionLimit": "At most {count} PBS connections may be configured.", + "username": "Enter the PBS user in user@realm format, for example root@pam.", + "password": "Enter the PBS user's password.", + "loginRejected": "PBS rejected the username or password.", + "invalidLogin": "PBS returned an invalid sign-in response.", + "tokenCreateDenied": "This PBS user cannot create API tokens.", + "invalidToken": "PBS returned an invalid API token.", + "auditRoleDenied": "This PBS user cannot grant the Audit role. Sign in as root@pam or another administrator.", + "notFound": "The PBS connection was not found. Reload the connection list.", + "readFailed": "Could not read PBS data. Check its service and permissions.", + "invalidList": "PBS returned an invalid data list.", + "localUnavailable": "PBS is not available on this host.", + "authRequired": "Enable local authentication in Monitor before managing remote PBS connections.", + "datastoreOffline": "The datastore is offline, in maintenance mode or its backing device is not mounted.", + "datastoreUnavailable": "The datastore filesystem is not available." + }, + "taskTypes": { + "backup": "Backup", "restore": "Restore", "verify": "Verification", + "verifyGroup": "Backup group verification", "verifySnapshot": "Snapshot verification", + "prune": "Prune", "garbageCollection": "Garbage collection", "sync": "Synchronization", + "updateCheck": "Check for updates", "terminal": "Terminal", "createDatastore": "Create datastore", + "other": "Other task ({type})" + }, + "states": { "ok": "OK", "unknown": "Unknown", "warnings": "Warnings: {count}", "warning": "Warning", "error": "Error" }, + "serviceStates": { "active": "Active", "inactive": "Inactive", "failed": "Failed", "activating": "Starting", "deactivating": "Stopping", "unknown": "Unknown" }, + "durationUnits": { "seconds": "{count} s", "minutes": "{count} min", "hours": "{count} h", "days": "{count} d" } }, "app": { "title": "ProxMenux Monitor", diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index 29ea7bd2..801b456e 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -8,7 +8,7 @@ "notAvailable": "Nedostupné", "serverOffline": "Server je offline", "supportProject": "Podporte projekt alebo prispejte vlastnými úpravami", - "descriptionPbs": "Proxmox Backup Server Dashboard" + "descriptionPbs": "Prehľad systému Proxmox Backup Server" }, "common": { "notAvailable": "Nedostupné", @@ -7299,35 +7299,35 @@ "title": "Proxmox Backup Server", "server": "Server PBS", "chooseServer": "Vybrať server PBS", - "local": "Miestny PBS", + "local": "Lokálny PBS", "thisNode": "PBS na tomto uzle", "updatedAt": "Aktualizované o {time}", "servicesActive": "Služby sú aktívne", - "detectedTitle": "PBS zistené v úložisku Proxmox", - "detectedHelp": "Tieto Proxmox Backup Servery sú nastavené ako úložisko záloh. Pripojte každý z nich API tokenom len na čítanie, aby ste tu videli jeho datastory, úlohy a joby.", + "detectedTitle": "PBS zistené v nastaveniach úložísk Proxmoxu", + "detectedHelp": "Tieto servery Proxmox Backup Server sú nastavené ako úložiská záloh. Pripoj ich pomocou API tokenu iba na čítanie a zobrazia sa tu ich dátové úložiská, úlohy a plány údržby.", "connect": "Pripojiť", "tasksTab": "Úlohy", "signInWith": "Prihlásiť sa pomocou", "methodPassword": "Používateľ a heslo", "methodToken": "API token", - "username": "Používateľ (pouzivatel@realm)", + "username": "Používateľ (user@realm)", "password": "Heslo", - "passwordHelp": "Heslo sa použije iba raz na vytvorenie API tokenu len na čítanie (rola Audit) pre tento Monitor. Uloží sa iba tento token, nikdy nie heslo. Najprv zapnite overovanie v Monitore.", + "passwordHelp": "Heslo sa použije iba raz na vytvorenie API tokenu iba na čítanie s rolou Audit pre tento Monitor. Uloží sa iba token, heslo sa neukladá. Najprv zapni overovanie v Monitore.", "cancel": "Zrušiť", "refresh": "Obnoviť", "manage": "Spravovať pripojenia PBS", "loadFailed": "Nepodarilo sa načítať stav PBS.", "loading": "Načítavajú sa údaje PBS…", - "noServer": "Pridajte pripojenie PBS na zobrazenie dátových úložísk, úloh a plánov.", - "permissionsHelp": "Pred uložením prihlasovacích údajov povoľte overovanie v Monitore. Použite vyhradený API token PBS s rolou Audit na / v ACL používateľa aj tokenu, so zapnutým dedením. Tento panel údaje PBS iba číta.", + "noServer": "Pridaj pripojenie PBS a zobrazia sa jeho dátové úložiská, úlohy a plány údržby.", + "permissionsHelp": "Pred uložením prihlasovacích údajov zapni overovanie v Monitore. Použi vyhradený API token PBS. Používateľ aj token musia mať v zozname prístupových oprávnení (ACL) rolu Audit na ceste / so zapnutým dedením. Tento panel údaje PBS iba číta.", "editConnection": "Pripojenie na úpravu", "add": "Pridať nové pripojenie", "name": "Názov", - "url": "HTTPS URL (pre pôvodný port PBS uveďte :8007)", + "url": "Adresa HTTPS (pre štandardný port PBS uveď :8007)", "token_id": "ID API tokenu (user@realm!token)", - "token_secret": "Tajný kľúč API tokenu (ponechajte prázdne, ak chcete zachovať uložený kľúč)", + "token_secret": "Tajný kľúč API tokenu (prázdne pole zachová uložený kľúč)", "fingerprint": "Odtlačok SHA256 certifikátu (voliteľný)", - "tlsHelp": "Pre bežné overenie certifikačnej autority a názvu hostiteľa ponechajte odtlačok prázdny. Pri certifikáte s vlastným podpisom skopírujte jeho odtlačok SHA256 z konzoly PBS alebo iného dôveryhodného kanála. Odtlačok sa kontroluje pred odoslaním prihlasovacích údajov.", + "tlsHelp": "Pri bežnom overovaní certifikačnej autority a názvu hostiteľa nechaj odtlačok prázdny. Pri certifikáte s vlastným podpisom skopíruj odtlačok SHA256 z konzoly PBS alebo iného dôveryhodného zdroja. Kontroluje sa ešte pred odoslaním prihlasovacích údajov.", "saved": "Pripojenie PBS bolo overené a uložené.", "saveFailed": "Nepodarilo sa uložiť pripojenie PBS.", "testing": "Overuje sa pripojenie…", @@ -7339,9 +7339,9 @@ "total": "Kapacita súborového systému", "used": "Použité", "free": "Dostupné", - "noDatastores": "Žiadne dostupné dátové úložiská. Skontrolujte konfiguráciu úložísk a oprávnenia používateľa a tokenu.", + "noDatastores": "Nie sú dostupné žiadne dátové úložiská. Skontroluj ich nastavenia a oprávnenia používateľa aj tokenu.", "tasks": "Nedávne úlohy", - "noTasks": "Neboli vrátené žiadne nedávne úlohy.", + "noTasks": "PBS nevrátil žiadne nedávne úlohy.", "task": "Úloha", "started": "Spustené", "status": "Stav", @@ -7351,7 +7351,7 @@ "schedule": "Plán", "sync": "Synchronizácia", "verify": "Overenie", - "prune": "Prerezávanie", + "prune": "Prerezávanie záloh", "overview": "Prehľad", "logs": "Systémové záznamy", "terminal": "Terminál", @@ -7362,13 +7362,61 @@ "memory": "Pamäť", "uptime": "Čas prevádzky", "capacityUnavailable": "Kapacita nie je k dispozícii.", - "datastoresUnavailable": "{count} nie je k dispozícii", + "datastoresUnavailable": "Nedostupné úložiská: {count}", "datastoresOnline": "Všetky dostupné", "moreDatastores": "+{count} ďalších dátových úložísk", - "estimatedFull": "Odhadovaný plný", - "estimatedFullUnknown": "Zatiaľ nie je dostatok údajov", + "estimatedFull": "Odhadované zaplnenie", + "estimatedFullUnknown": "Zatiaľ sa nedá odhadnúť", "duration": "Trvanie", - "noJobs": "Nie sú nakonfigurované žiadne plány synchronizácie, overovania ani odstraňovania." + "noJobs": "Nie sú nastavené žiadne plány synchronizácie, overovania ani prerezávania záloh.", + "removeFailed": "Nepodarilo sa odstrániť pripojenie PBS.", + "savedRefreshFailed": "Pripojenie PBS sa uložilo, ale nepodarilo sa obnoviť zoznam pripojení. Pred ďalšou zmenou obnov stránku.", + "removedRefreshFailed": "Pripojenie PBS sa odstránilo, ale nepodarilo sa obnoviť zoznam pripojení. Obnov stránku.", + "technicalDetails": "Technické podrobnosti", + "datastoreType": "Dátové úložisko", + "errors": { + "configRead": "Nepodarilo sa načítať uložené pripojenia PBS. Skontroluj konfiguračný súbor a oprávnenia.", + "invalidConfig": "Nastavenia pripojenia PBS nie sú platné. Skontroluj formulár a skús to znova.", + "invalidField": "Niektoré pole pripojenia nie je platné alebo je príliš dlhé.", + "invalidUrl": "Použi adresu HTTPS bez vložených prihlasovacích údajov, parametrov za otáznikom a časti za znakom #.", + "nameRequired": "Zadaj názov servera s najviac 128 znakmi.", + "tokenIdFormat": "Zadaj ID API tokenu PBS vo formáte user@realm!token.", + "tokenSecret": "Zadaj platný tajný kľúč API tokenu bez medzier a riadiacich znakov.", + "invalidTokenId": "ID API tokenu môže obsahovať iba znaky ASCII.", + "invalidFingerprint": "Zadaj odtlačok SHA256 certifikátu: 64 šestnástkových znakov, prípadne oddelených dvojbodkami.", + "fingerprintMismatch": "Certifikát PBS sa nezhoduje s odtlačkom. Prihlasovacie údaje sa neodoslali.", + "tokenRejected": "PBS odmietol API token alebo jeho oprávnenia. Skontroluj zoznam prístupových oprávnení (ACL) používateľa aj tokenu.", + "invalidResponse": "PBS vrátil neočakávanú odpoveď. Presmerovania sa nenasledujú.", + "invalidJson": "PBS vrátil neplatný dokument JSON.", + "connectFailed": "Nepodarilo sa pripojiť k PBS cez overené HTTPS. Skontroluj adresu, certifikát a službu.", + "invalidId": "ID pripojenia PBS nie je platné. Obnov zoznam pripojení.", + "invalidDatastores": "PBS vrátil neplatný zoznam dátových úložísk.", + "connectionLimit": "Môžeš nastaviť najviac {count} pripojení PBS.", + "username": "Zadaj používateľa PBS vo formáte user@realm, napríklad root@pam.", + "password": "Zadaj heslo používateľa PBS.", + "loginRejected": "PBS odmietol používateľské meno alebo heslo.", + "invalidLogin": "PBS vrátil neplatnú odpoveď na prihlásenie.", + "tokenCreateDenied": "Tento používateľ PBS nemôže vytvárať API tokeny.", + "invalidToken": "PBS vrátil neplatný API token.", + "auditRoleDenied": "Tento používateľ PBS nemôže prideliť rolu Audit. Prihlás sa ako root@pam alebo iný správca.", + "notFound": "Pripojenie PBS sa nenašlo. Obnov zoznam pripojení.", + "readFailed": "Nepodarilo sa načítať údaje PBS. Skontroluj službu a oprávnenia.", + "invalidList": "PBS vrátil neplatný zoznam údajov.", + "localUnavailable": "PBS na tomto hostiteľovi nie je dostupný.", + "authRequired": "Pred správou vzdialených pripojení PBS zapni lokálne overovanie v Monitore.", + "datastoreOffline": "Dátové úložisko je odpojené, v režime údržby alebo jeho zariadenie nie je pripojené.", + "datastoreUnavailable": "Súborový systém dátového úložiska nie je dostupný." + }, + "taskTypes": { + "backup": "Zálohovanie", "restore": "Obnova", "verify": "Overovanie", + "verifyGroup": "Overovanie skupiny záloh", "verifySnapshot": "Overovanie snímky", + "prune": "Prerezávanie záloh", "garbageCollection": "Čistenie nepoužívaných dát", "sync": "Synchronizácia", + "updateCheck": "Kontrola aktualizácií", "terminal": "Terminál", "createDatastore": "Vytvorenie dátového úložiska", + "other": "Iná úloha ({type})" + }, + "states": { "ok": "V poriadku", "unknown": "Neznámy", "warnings": "Upozornenia: {count}", "warning": "Upozornenie", "error": "Chyba" }, + "serviceStates": { "active": "Aktívna", "inactive": "Neaktívna", "failed": "Zlyhala", "activating": "Spúšťa sa", "deactivating": "Zastavuje sa", "unknown": "Neznámy stav" }, + "durationUnits": { "seconds": "{count} s", "minutes": "{count} min", "hours": "{count} h", "days": "{count} d" } }, "monitorLinks": { "groups": { diff --git a/AppImage/scripts/flask_pbs_routes.py b/AppImage/scripts/flask_pbs_routes.py index 2c18f8b8..1ecb148c 100644 --- a/AppImage/scripts/flask_pbs_routes.py +++ b/AppImage/scripts/flask_pbs_routes.py @@ -8,6 +8,10 @@ from jwt_middleware import require_auth, require_admin_scope pbs_bp = Blueprint("pbs", __name__) +def pbs_error(exc, status): + return jsonify(error=str(exc), **exc.problem()), status + + @pbs_bp.after_request def no_cache(response): response.headers["Cache-Control"] = "no-store" @@ -27,7 +31,7 @@ def servers(): servers = pbs.public_servers() return jsonify(servers=servers, local_available=pbs.local_available(), detected=_detected(servers)) except pbs.PBSError as exc: - return jsonify(error=str(exc)), 500 + return pbs_error(exc, 500) def _detected(servers): @@ -57,42 +61,42 @@ def _protected_configuration(): @require_admin_scope def save_server(): if not _protected_configuration(): - return jsonify(error="Enable local authentication before saving remote PBS credentials."), 409 + return jsonify(error="Enable local authentication before saving remote PBS credentials.", code="pbs_auth_required"), 409 try: identifier = pbs.save_server(request.get_json(silent=True)) return jsonify(success=True, id=identifier, servers=pbs.public_servers()) except pbs.PBSError as exc: - return jsonify(error=str(exc)), 400 + return pbs_error(exc, 400) except OSError: - return jsonify(error="Could not save PBS connection."), 500 + return jsonify(error="Could not save PBS connection.", code="pbs_save_failed"), 500 @pbs_bp.route("/api/pbs/servers/login", methods=["POST"]) @require_admin_scope def connect_with_password(): if not _protected_configuration(): - return jsonify(error="Enable local authentication before saving remote PBS credentials."), 409 + return jsonify(error="Enable local authentication before saving remote PBS credentials.", code="pbs_auth_required"), 409 try: identifier = pbs.connect_with_password(request.get_json(silent=True)) return jsonify(success=True, id=identifier, servers=pbs.public_servers()) except pbs.PBSError as exc: - return jsonify(error=str(exc)), 400 + return pbs_error(exc, 400) except OSError: - return jsonify(error="Could not save PBS connection."), 500 + return jsonify(error="Could not save PBS connection.", code="pbs_save_failed"), 500 @pbs_bp.route("/api/pbs/servers/", methods=["DELETE"]) @require_admin_scope def delete_server(identifier): if not _protected_configuration(): - return jsonify(error="Enable local authentication before changing PBS connections."), 409 + return jsonify(error="Enable local authentication before changing PBS connections.", code="pbs_auth_required"), 409 try: pbs.delete_server(identifier) return jsonify(success=True) except pbs.PBSError as exc: - return jsonify(error=str(exc)), 400 + return pbs_error(exc, 400) except OSError: - return jsonify(error="Could not remove PBS connection."), 500 + return jsonify(error="Could not remove PBS connection.", code="pbs_remove_failed"), 500 @pbs_bp.route("/api/pbs/status") @@ -101,4 +105,4 @@ def status(): try: return jsonify(pbs.get_status(request.args.get("server", "local"))) except pbs.PBSError as exc: - return jsonify(error=str(exc)), 502 + return pbs_error(exc, 502) diff --git a/AppImage/scripts/pbs_manager.py b/AppImage/scripts/pbs_manager.py index 7ec910d1..d4a12ad4 100644 --- a/AppImage/scripts/pbs_manager.py +++ b/AppImage/scripts/pbs_manager.py @@ -39,7 +39,13 @@ MAX_RESPONSE = 4 * 1024 * 1024 class PBSError(ValueError): - pass + def __init__(self, message, *, code="pbs_unknown", params=None): + super().__init__(message) + self.code = code + self.params = params + + def problem(self): + return {"code": self.code, **({"params": self.params} if self.params else {})} def get_platform(): @@ -87,7 +93,7 @@ def _load_servers(): except FileNotFoundError: return [] except (OSError, ValueError) as exc: - raise PBSError("Could not read the PBS server configuration.") from exc + raise PBSError("Could not read the PBS server configuration.", code="pbs_config_read") from exc def _save_servers(servers): @@ -113,11 +119,11 @@ def public_servers(): def _validate_server(data, previous=None, fields=("name", "url", "token_id", "token_secret", "fingerprint")): if not isinstance(data, dict): - raise PBSError("Invalid PBS server configuration.") + raise PBSError("Invalid PBS server configuration.", code="pbs_invalid_config") previous = previous or {} row = {field: data.get(field, previous.get(field, "")) for field in fields} if any(not isinstance(value, str) or len(value) > 4096 for value in row.values()): - raise PBSError("Invalid PBS connection field.") + raise PBSError("Invalid PBS connection field.", code="pbs_invalid_field") row["name"] = row["name"].strip() row["url"] = row["url"].strip().rstrip("/") try: @@ -127,26 +133,26 @@ def _validate_server(data, previous=None, fields=("name", "url", "token_id", "to or parts.query or parts.fragment): raise ValueError() except ValueError as exc: - raise PBSError("Use an HTTPS PBS URL without embedded credentials, query or fragment.") from exc + raise PBSError("Use an HTTPS PBS URL without embedded credentials, query or fragment.", code="pbs_invalid_url") from exc if not row["name"] or len(row["name"]) > 128: - raise PBSError("A server name of at most 128 characters is required.") + raise PBSError("A server name of at most 128 characters is required.", code="pbs_name_required") if "token_id" not in row: return _validated_fingerprint(row) row["token_id"] = row["token_id"].strip() if not re.fullmatch(r"[^\s:!]+@[^\s:!]+![^\s:!]+", row["token_id"]): - raise PBSError("Enter a PBS API token ID in user@realm!token format.") + raise PBSError("Enter a PBS API token ID in user@realm!token format.", code="pbs_token_id_format") if (not row["token_secret"] or not row["token_secret"].isascii() or any(char.isspace() or ord(char) < 32 for char in row["token_secret"])): - raise PBSError("A valid API token secret is required.") + raise PBSError("A valid API token secret is required.", code="pbs_token_secret") if not row["token_id"].isascii(): - raise PBSError("Invalid API token ID.") + raise PBSError("Invalid API token ID.", code="pbs_invalid_token_id") return _validated_fingerprint(row) def _validated_fingerprint(row): fingerprint = row["fingerprint"].strip().lower().replace(":", "") if fingerprint and not re.fullmatch("[a-f0-9]{64}", fingerprint): - raise PBSError("The certificate fingerprint must be SHA256 (64 hex characters).") + raise PBSError("The certificate fingerprint must be SHA256 (64 hex characters).", code="pbs_invalid_fingerprint") row["fingerprint"] = fingerprint return row @@ -158,7 +164,8 @@ def _remote_get(server, endpoint): def _remote_request(server, method, endpoint, headers, form=None, - rejected="PBS rejected the API token or its permissions. Check both the user and token ACLs."): + rejected="PBS rejected the API token or its permissions. Check both the user and token ACLs.", + *, rejected_code="pbs_token_rejected"): """One request to PBS; credentials leave only after the certificate is checked.""" parts = urlsplit(server["url"]) fingerprint = server.get("fingerprint") @@ -175,7 +182,7 @@ def _remote_request(server, method, endpoint, headers, form=None, if fingerprint: actual = hashlib.sha256(connection.sock.getpeercert(binary_form=True)).hexdigest() if not hmac.compare_digest(actual, fingerprint): - raise PBSError("PBS certificate fingerprint does not match. No credentials were sent.") + raise PBSError("PBS certificate fingerprint does not match. No credentials were sent.", code="pbs_fingerprint_mismatch") path = parts.path.rstrip("/") + "/api2/json" + endpoint body = urlencode(form) if form is not None else None request_headers = {"Accept": "application/json", **headers} @@ -185,17 +192,17 @@ def _remote_request(server, method, endpoint, headers, form=None, response = connection.getresponse() content = response.read(MAX_RESPONSE + 1) if response.status in (401, 403): - raise PBSError(rejected) + raise PBSError(rejected, code=rejected_code) if response.status != 200 or len(content) > MAX_RESPONSE: - raise PBSError("PBS returned an invalid response. Redirects are not followed.") + raise PBSError("PBS returned an invalid response. Redirects are not followed.", code="pbs_invalid_response") document = json.loads(content) if not isinstance(document, dict) or "data" not in document: - raise PBSError("PBS returned an invalid JSON document.") + raise PBSError("PBS returned an invalid JSON document.", code="pbs_invalid_json") return document["data"] except PBSError: raise except (OSError, http.client.HTTPException, ValueError) as exc: - raise PBSError("Could not connect to PBS over verified HTTPS. Check the address, certificate and service.") from exc + raise PBSError("Could not connect to PBS over verified HTTPS. Check the address, certificate and service.", code="pbs_connect_failed") from exc finally: connection.close() @@ -203,19 +210,19 @@ def _remote_request(server, method, endpoint, headers, form=None, def save_server(data): identifier = data.get("id") if isinstance(data, dict) else None if identifier is not None and not re.fullmatch(r"[A-Za-z0-9_-]{1,64}", str(identifier)): - raise PBSError("Invalid PBS server ID.") + raise PBSError("Invalid PBS server ID.", code="pbs_invalid_id") with _lock: previous = next((row for row in _load_servers() if row["id"] == identifier), None) row = _validate_server(data, previous) # /version may be public; validate credentials on the protected status API. usage = _remote_get(row, "/status/datastore-usage") if not isinstance(usage, list): - raise PBSError("PBS returned an invalid datastore list.") + raise PBSError("PBS returned an invalid datastore list.", code="pbs_invalid_datastores") row.update(id=identifier or "pbs-" + secrets.token_hex(6), revision=secrets.token_hex(16)) with _lock: servers = _load_servers() if previous is None and len(servers) >= 32: - raise PBSError("At most 32 PBS connections may be configured.") + raise PBSError("At most 32 PBS connections may be configured.", code="pbs_connection_limit", params={"count": 32}) servers = [existing for existing in servers if existing["id"] != row["id"]] _save_servers([*servers, row]) return row["id"] @@ -228,27 +235,27 @@ def connect_with_password(data): username = data.get("username") if isinstance(data, dict) else None password = data.get("password") if isinstance(data, dict) else None if not isinstance(username, str) or not re.fullmatch(r"[^\s:!@]+@[^\s:!@]+", username.strip()): - raise PBSError("Enter the PBS user as user@realm, for example root@pam.") + raise PBSError("Enter the PBS user as user@realm, for example root@pam.", code="pbs_username") if not isinstance(password, str) or not password or len(password) > 4096: - raise PBSError("Enter the password of the PBS user.") + raise PBSError("Enter the password of the PBS user.", code="pbs_password") ticket = _remote_request(endpoint, "POST", "/access/ticket", {}, {"username": username.strip(), "password": password}, - rejected="PBS rejected the user name or password.") + rejected="PBS rejected the user name or password.", rejected_code="pbs_login_rejected") if not isinstance(ticket, dict) or not ticket.get("ticket") or not ticket.get("CSRFPreventionToken"): - raise PBSError("PBS returned an invalid sign-in response.") + raise PBSError("PBS returned an invalid sign-in response.", code="pbs_invalid_login") session = {"Cookie": "PBSAuthCookie=" + quote(ticket["ticket"], safe=""), "CSRFPreventionToken": ticket["CSRFPreventionToken"]} user = str(ticket.get("username") or username.strip()) name = "proxmenux-" + secrets.token_hex(4) token_path = f"/access/users/{quote(user, safe='')}/token/{name}" created = _remote_request(endpoint, "POST", token_path, session, {"comment": "ProxMenux Monitor, read-only"}, - rejected="This PBS user cannot create API tokens.") + rejected="This PBS user cannot create API tokens.", rejected_code="pbs_token_create_denied") if not isinstance(created, dict) or not created.get("tokenid") or not created.get("value"): - raise PBSError("PBS returned an invalid API token.") + raise PBSError("PBS returned an invalid API token.", code="pbs_invalid_token") try: _remote_request(endpoint, "PUT", "/access/acl", session, {"path": "/", "role": "Audit", "auth-id": created["tokenid"], "propagate": "1"}, - rejected="This PBS user cannot grant the Audit role. Sign in with root@pam or an administrator.") + rejected="This PBS user cannot grant the Audit role. Sign in with root@pam or an administrator.", rejected_code="pbs_audit_role_denied") return save_server({**endpoint, "token_id": created["tokenid"], "token_secret": created["value"]}) except PBSError: try: @@ -263,7 +270,7 @@ def delete_server(identifier): servers = _load_servers() remaining = [row for row in servers if row["id"] != identifier] if len(remaining) == len(servers): - raise PBSError("PBS server not found.") + raise PBSError("PBS server not found.", code="pbs_not_found") _save_servers(remaining) @@ -276,7 +283,7 @@ def _cli(*arguments): raise ValueError() return data except (OSError, subprocess.SubprocessError, ValueError) as exc: - raise PBSError("Could not read PBS " + " ".join(arguments[:2]) + ". Check its service and permissions.") from exc + raise PBSError("Could not read PBS " + " ".join(arguments[:2]) + ". Check its service and permissions.", code="pbs_read_failed") from exc def _local_api(path): @@ -290,7 +297,7 @@ def _local_api(path): raise ValueError() return data except (OSError, subprocess.SubprocessError, ValueError) as exc: - raise PBSError("Could not read PBS " + path + ". Check its service and permissions.") from exc + raise PBSError("Could not read PBS " + path + ". Check its service and permissions.", code="pbs_read_failed") from exc def _local_datastores(): @@ -305,12 +312,14 @@ def _local_datastores(): inactive = store.get("backing-device") and not (path and os.path.ismount(path)) if not path or inactive or store.get("maintenance-mode"): row["error"] = "Datastore is offline, in maintenance or has no mounted backing device." + row["error_code"] = "pbs_datastore_offline" else: try: capacity = shutil.disk_usage(path) row.update(total=capacity.total, used=capacity.used, avail=capacity.free) except OSError: row["error"] = "Datastore filesystem is not available." + row["error_code"] = "pbs_datastore_unavailable" result.append(row) return result @@ -331,10 +340,10 @@ def get_status(identifier): cached = _status_cache.get(identifier) if cached and cached[0] > time.monotonic(): return cached[1] - errors = {} + errors, error_details = {}, {} if identifier == "local": if not local_available(): - raise PBSError("The local host is not a PBS server.") + raise PBSError("The local host is not a PBS server.", code="pbs_local_unavailable") name, version = get_platform().get("hostname") or socket.gethostname(), _local_version() services = _local_services() operations = {"datastores": _local_datastores, @@ -346,7 +355,7 @@ def get_status(identifier): with _lock: server = next((row for row in _load_servers() if row["id"] == identifier), None) if not server: - raise PBSError("PBS server not found.") + raise PBSError("PBS server not found.", code="pbs_not_found") metadata = _remote_get(server, "/version") name, version = server["name"], metadata.get("version") if isinstance(metadata, dict) else None services = {} @@ -354,18 +363,19 @@ def get_status(identifier): "sync": "/admin/sync", "verify": "/admin/verify", "prune": "/admin/prune"} operations = {key: (lambda endpoint=path: _remote_get(server, endpoint)) for key, path in endpoints.items()} result = {"server": identifier, "name": name, "version": version, "services": services, - "fetched_at": int(time.time()), "errors": errors} + "fetched_at": int(time.time()), "errors": errors, "error_details": error_details} with ThreadPoolExecutor(max_workers=5) as executor: futures = {key: executor.submit(operation) for key, operation in operations.items()} for key, future in futures.items(): try: data = future.result() if not isinstance(data, list) or not all(isinstance(row, dict) for row in data): - raise PBSError("PBS returned an invalid list.") + raise PBSError("PBS returned an invalid list.", code="pbs_invalid_list") result[key] = [{**row, "name": row.get("store", row.get("name"))} for row in data] if key == "datastores" else data except PBSError as exc: result[key] = None # Unknown/forbidden is never presented as zero. errors[key] = str(exc) + error_details[key] = exc.problem() with _lock: if len(_status_cache) >= 33: _status_cache.clear() diff --git a/AppImage/scripts/tests/test_pbs.py b/AppImage/scripts/tests/test_pbs.py index cf932a8e..e86c38ac 100644 --- a/AppImage/scripts/tests/test_pbs.py +++ b/AppImage/scripts/tests/test_pbs.py @@ -205,7 +205,7 @@ class PBSManagerTests(unittest.TestCase): def test_password_sign_in_keeps_only_a_read_only_token(self): calls = [] - def request(server, method, endpoint, headers, form=None, rejected=""): + def request(server, method, endpoint, headers, form=None, rejected="", **kwargs): calls.append((method, endpoint, form)) if endpoint == "/access/ticket": return {"ticket": "PBS:root@pam:ticket", "CSRFPreventionToken": "csrf", "username": "root@pam"} @@ -227,7 +227,7 @@ class PBSManagerTests(unittest.TestCase): def test_password_sign_in_removes_its_token_when_the_role_cannot_be_granted(self): calls = [] - def request(server, method, endpoint, headers, form=None, rejected=""): + def request(server, method, endpoint, headers, form=None, rejected="", **kwargs): calls.append((method, endpoint)) if endpoint == "/access/ticket": return {"ticket": "t", "CSRFPreventionToken": "c"} @@ -351,15 +351,17 @@ class TLSTests(unittest.TestCase): def test_bad_pin_and_untrusted_ca_send_no_http_credentials(self): for fingerprint in ('0' * 64, ''): - with self.subTest(fingerprint=fingerprint), self.assertRaises(pbs.PBSError): + with self.subTest(fingerprint=fingerprint), self.assertRaises(pbs.PBSError) as caught: pbs._remote_get({**self.connection, "fingerprint": fingerprint}, '/status/datastore-usage') + self.assertEqual(caught.exception.code, "pbs_fingerprint_mismatch" if fingerprint else "pbs_connect_failed") self.assertEqual(self.requests, []) def test_bad_credentials_and_redirects_are_rejected(self): for status in (401, 403, 302): type(self).response_status = status - with self.subTest(status=status), self.assertRaises(pbs.PBSError): + with self.subTest(status=status), self.assertRaises(pbs.PBSError) as caught: pbs._remote_get(self.connection, '/status/datastore-usage') + self.assertEqual(caught.exception.code, "pbs_token_rejected" if status in (401, 403) else "pbs_invalid_response") if __name__ == '__main__': diff --git a/AppImage/scripts/tests/test_pbs_i18n.py b/AppImage/scripts/tests/test_pbs_i18n.py new file mode 100644 index 00000000..42a629c7 --- /dev/null +++ b/AppImage/scripts/tests/test_pbs_i18n.py @@ -0,0 +1,196 @@ +"""PBS localization metadata must preserve routes, security and persistence.""" +import importlib.util +import json +from pathlib import Path +import sys +import tempfile +import types +import unittest +from unittest import mock +from flask import Flask + +ROOT = Path(__file__).resolve().parents[3] +sys.path.insert(0, str(ROOT / "AppImage/scripts")) +import auth_manager +import pbs_manager as pbs +REAL_REMOTE_REQUEST = pbs._remote_request + + +class PBSI18nTests(unittest.TestCase): + def setUp(self): + temp = tempfile.TemporaryDirectory(); self.addCleanup(temp.cleanup) + self.root = Path(temp.name) + for patch in [mock.patch.multiple(auth_manager, CONFIG_DIR=self.root, AUTH_CONFIG_FILE=self.root / "auth.json"), + mock.patch.object(pbs, "_remote_get", return_value=[]), + mock.patch.object(pbs, "_remote_request"), + mock.patch.object(pbs, "local_available", return_value=False)]: + patch.start(); self.addCleanup(patch.stop) + (self.root / "auth.json").write_text(json.dumps({"enabled": True, "password_hash": "fixture"})) + self.server = {"name": "Fixture", "url": "https://pbs.invalid:8007", "token_id": "audit@pbs!monitor", "token_secret": "fake-fixture-secret", "fingerprint": ""} + self.id = pbs.save_server(self.server) + pbs._remote_get.reset_mock(); pbs._status_cache.clear() + auth = types.SimpleNamespace(load_auth_config=auth_manager.load_auth_config, verify_token=lambda _: "fixture", + verify_token_full=lambda token: ("fixture", "full_admin" if token == "admin" else "read_only")) + spec = importlib.util.spec_from_file_location("fixture_pbs_jwt", ROOT / "AppImage/scripts/jwt_middleware.py") + middleware = importlib.util.module_from_spec(spec) + with mock.patch.dict(sys.modules, {"auth_manager": auth}): spec.loader.exec_module(middleware) + spec = importlib.util.spec_from_file_location("fixture_pbs_routes", ROOT / "AppImage/scripts/flask_pbs_routes.py") + routes = importlib.util.module_from_spec(spec) + with mock.patch.dict(sys.modules, {"jwt_middleware": middleware}): spec.loader.exec_module(routes) + app = Flask(__name__); app.register_blueprint(routes.pbs_bp); self.client = app.test_client() + + def send(self, method="POST", path="/api/pbs/servers", data=None, role="admin"): + return self.client.open(path, method=method, json=data, headers={"Authorization": "Bearer " + role} if role else {}) + + def reject(self, data, message, code, path="/api/pbs/servers"): + before = (self.root / "pbs_servers.json").read_bytes() + with mock.patch.object(pbs, "_save_servers") as save: + response = self.send(path=path, data=data) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json, {"error": message, "code": code}) + self.assertEqual(response.headers["Cache-Control"], "no-store") + save.assert_not_called(); pbs._remote_get.assert_not_called(); pbs._remote_request.assert_not_called() + self.assertEqual((self.root / "pbs_servers.json").read_bytes(), before) + + def test_invalid_input_is_localizable_before_any_remote_or_save_effect(self): + cases = [ + (None, "Invalid PBS server configuration.", "pbs_invalid_config"), + ({**self.server, "name": []}, "Invalid PBS connection field.", "pbs_invalid_field"), + ({**self.server, "url": "http://pbs.invalid"}, "Use an HTTPS PBS URL without embedded credentials, query or fragment.", "pbs_invalid_url"), + ({**self.server, "name": ""}, "A server name of at most 128 characters is required.", "pbs_name_required"), + ({**self.server, "token_id": "bad"}, "Enter a PBS API token ID in user@realm!token format.", "pbs_token_id_format"), + ({**self.server, "token_secret": "bad\nsecret"}, "A valid API token secret is required.", "pbs_token_secret"), + ({**self.server, "token_id": "človek@pbs!monitor"}, "Invalid API token ID.", "pbs_invalid_token_id"), + ({**self.server, "fingerprint": "invalid"}, "The certificate fingerprint must be SHA256 (64 hex characters).", "pbs_invalid_fingerprint"), + ({**self.server, "id": "../../bad"}, "Invalid PBS server ID.", "pbs_invalid_id"), + ] + for data, message, code in cases: + with self.subTest(code=code): self.reject(data, message, code) + for data, message, code in [ + ({**self.server, "username": "missing-realm", "password": "fake"}, "Enter the PBS user as user@realm, for example root@pam.", "pbs_username"), + ({**self.server, "username": "root@pam", "password": ""}, "Enter the password of the PBS user.", "pbs_password"), + ]: + with self.subTest(code=code): self.reject(data, message, code, "/api/pbs/servers/login") + + def test_authentication_and_scope_guards_precede_all_mutations(self): + before = (self.root / "pbs_servers.json").read_bytes() + save_patch = mock.patch.object(pbs, "_save_servers") + save = save_patch.start(); self.addCleanup(save_patch.stop) + for path, method in [("/api/pbs/servers", "POST"), ("/api/pbs/servers/login", "POST"), ("/api/pbs/servers/" + self.id, "DELETE")]: + for role, status in [(None, 401), ("reader", 403)]: + self.assertEqual(self.send(method, path, self.server, role).status_code, status) + (self.root / "auth.json").write_text(json.dumps({"enabled": False})) + for path in ("/api/pbs/servers", "/api/pbs/servers/login"): + response = self.send(path=path, data=self.server) + self.assertEqual(response.status_code, 409) + self.assertEqual(response.json, {"error": "Enable local authentication before saving remote PBS credentials.", "code": "pbs_auth_required"}) + response = self.send("DELETE", "/api/pbs/servers/" + self.id) + self.assertEqual(response.status_code, 409) + self.assertEqual(response.json["code"], "pbs_auth_required") + pbs._remote_get.assert_not_called(); pbs._remote_request.assert_not_called() + save.assert_not_called() + self.assertEqual((self.root / "pbs_servers.json").read_bytes(), before) + + def test_corrupt_configuration_missing_server_and_local_unavailable_keep_status(self): + response = self.send("DELETE", "/api/pbs/servers/absent") + self.assertEqual((response.status_code, response.json), (400, {"error": "PBS server not found.", "code": "pbs_not_found"})) + for identifier, code in [("absent", "pbs_not_found"), ("local", "pbs_local_unavailable")]: + response = self.send("GET", "/api/pbs/status?server=" + identifier) + self.assertEqual(response.status_code, 502); self.assertEqual(response.json["code"], code) + (self.root / "pbs_servers.json").write_text("invalid json") + response = self.send("GET", "/api/pbs/servers") + self.assertEqual((response.status_code, response.json), (500, {"error": "Could not read the PBS server configuration.", "code": "pbs_config_read"})) + pbs._remote_get.assert_not_called(); pbs._remote_request.assert_not_called() + + def test_save_delete_failures_leave_previous_bytes_and_do_not_leak_details(self): + for path, method, data, code, message in [ + ("/api/pbs/servers", "POST", {**self.server, "id": self.id}, "pbs_save_failed", "Could not save PBS connection."), + ("/api/pbs/servers/" + self.id, "DELETE", None, "pbs_remove_failed", "Could not remove PBS connection."), + ]: + before = (self.root / "pbs_servers.json").read_bytes() + with self.subTest(method=method), mock.patch.object(pbs.os, "replace", side_effect=OSError("private filesystem detail")): + response = self.send(method, path, data) + self.assertEqual((response.status_code, response.json), (500, {"error": message, "code": code})) + self.assertEqual((self.root / "pbs_servers.json").read_bytes(), before) + + def test_partial_status_preserves_legacy_error_null_and_cache_with_additive_metadata(self): + def remote(server, endpoint): + if endpoint == "/version": return {"version": "4.2.5"} + if endpoint == "/admin/verify": raise pbs.PBSError("Fixture access denied", code="pbs_token_rejected") + if endpoint == "/admin/prune": return {"invalid": "list"} + return [] + with mock.patch.object(pbs, "_remote_get", side_effect=remote) as remote_call: + result = self.send("GET", "/api/pbs/status?server=" + self.id) + count = remote_call.call_count + self.assertEqual(self.send("GET", "/api/pbs/status?server=" + self.id).json, result.json) + self.assertEqual(remote_call.call_count, count) + self.assertEqual(result.status_code, 200) + self.assertIsNone(result.json["verify"]); self.assertIsNone(result.json["prune"]) + self.assertEqual(result.json["sync"], []) + self.assertEqual(result.json["errors"]["verify"], "Fixture access denied") + self.assertEqual(result.json["error_details"]["verify"], {"code": "pbs_token_rejected"}) + self.assertEqual(result.json["error_details"]["prune"], {"code": "pbs_invalid_list"}) + + def test_local_datastore_errors_and_command_errors_keep_legacy_messages(self): + with mock.patch.object(pbs, "_cli", return_value=[{"name": "offline", "path": "/fixture", "backing-device": "fixture"}]), \ + mock.patch.object(pbs.os.path, "ismount", return_value=False), mock.patch.object(pbs.shutil, "disk_usage") as usage: + store = pbs._local_datastores()[0] + self.assertEqual(store["error"], "Datastore is offline, in maintenance or has no mounted backing device.") + self.assertEqual(store["error_code"], "pbs_datastore_offline"); usage.assert_not_called(); self.assertIsNone(store["total"]) + with mock.patch.object(pbs.subprocess, "run", side_effect=OSError("fixture")): + for fn, args in [(pbs._cli, ("task", "list")), (pbs._local_api, ("/admin/sync",))]: + with self.subTest(args=args), self.assertRaises(pbs.PBSError) as caught: fn(*args) + self.assertEqual(caught.exception.code, "pbs_read_failed") + self.assertIn("Check its service and permissions.", str(caught.exception)) + + def test_remote_failure_codes_follow_the_actual_request_and_keep_english(self): + cases = [ + (401, b'{}', "pbs_token_rejected", "PBS rejected the API token or its permissions. Check both the user and token ACLs."), + (302, b'{}', "pbs_invalid_response", "PBS returned an invalid response. Redirects are not followed."), + (200, b'{}', "pbs_invalid_json", "PBS returned an invalid JSON document."), + (200, b'invalid-json', "pbs_connect_failed", "Could not connect to PBS over verified HTTPS. Check the address, certificate and service."), + ] + for status, body, code, message in cases: + connection = mock.Mock(); connection.getresponse.return_value.status = status + connection.getresponse.return_value.read.return_value = body + with self.subTest(code=code), mock.patch.object(pbs.http.client, "HTTPSConnection", return_value=connection), \ + self.assertRaises(pbs.PBSError) as caught: + REAL_REMOTE_REQUEST(self.server, "GET", "/version", {}) + self.assertEqual((str(caught.exception), caught.exception.problem()), (message, {"code": code})) + connection.close.assert_called_once() + for code in ("pbs_login_rejected", "pbs_token_create_denied", "pbs_audit_role_denied"): + connection = mock.Mock(); connection.getresponse.return_value.status = 403 + connection.getresponse.return_value.read.return_value = b'{}' + with self.subTest(code=code), mock.patch.object(pbs.http.client, "HTTPSConnection", return_value=connection), self.assertRaises(pbs.PBSError) as caught: + REAL_REMOTE_REQUEST(self.server, "POST", "/access/ticket", {}, rejected="Original context", rejected_code=code) + self.assertEqual((str(caught.exception), caught.exception.code), ("Original context", code)) + + def test_connection_limit_and_invalid_datastore_response_do_not_save(self): + before = (self.root / "pbs_servers.json").read_bytes() + with mock.patch.object(pbs, "_save_servers") as save, mock.patch.object(pbs, "_load_servers", return_value=[{"id": str(i)} for i in range(32)]): + response = self.send(data=self.server) + self.assertEqual(response.json, {"error": "At most 32 PBS connections may be configured.", "code": "pbs_connection_limit", "params": {"count": 32}}) + self.assertEqual(response.status_code, 400); save.assert_not_called() + pbs._remote_get.return_value = {} + response = self.send(data=self.server) + self.assertEqual(response.json["code"], "pbs_invalid_datastores") + self.assertEqual((self.root / "pbs_servers.json").read_bytes(), before) + + def test_login_failures_keep_saved_state_and_cleanup_a_created_token(self): + before = (self.root / "pbs_servers.json").read_bytes() + login = {"name": "Fixture", "url": "https://pbs.invalid", "username": "root@pam", "password": "fake"} + for stage, code in [("ticket", "pbs_invalid_login"), ("token", "pbs_invalid_token"), ("acl", "pbs_audit_role_denied")]: + calls = [] + def request(server, method, endpoint, headers, form=None, rejected="", rejected_code="pbs_token_rejected"): + calls.append((method, endpoint)) + if endpoint == "/access/ticket": return None if stage == "ticket" else {"ticket": "fake", "CSRFPreventionToken": "fake"} + if method == "POST": return None if stage == "token" else {"tokenid": "audit@pbs!fixture", "value": "fake"} + if method == "PUT": raise pbs.PBSError(rejected, code=rejected_code) + pbs._remote_request.side_effect = request + response = self.send(path="/api/pbs/servers/login", data=login) + self.assertEqual(response.status_code, 400); self.assertEqual(response.json["code"], code) + self.assertEqual((self.root / "pbs_servers.json").read_bytes(), before) + self.assertEqual(sum(method == "DELETE" for method, _ in calls), 1 if stage == "acl" else 0) + + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_pbs_i18n.cjs b/tests/test_pbs_i18n.cjs new file mode 100644 index 00000000..1712b9d8 --- /dev/null +++ b/tests/test_pbs_i18n.cjs @@ -0,0 +1,134 @@ +// Shipped PBS code with only transport, hooks and UI primitives replaced. +const assert = require('node:assert/strict'), fs = require('node:fs'), vm = require('node:vm'); +const ts = require('typescript'); +const compile = source => ts.transpileModule(source, {compilerOptions: {module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, jsx: ts.JsxEmit.ReactJSX}}).outputText; +const catalogs = Object.fromEntries(['en','sk'].map(l => [l, JSON.parse(fs.readFileSync(`AppImage/messages/${l}/common.json`))])); +const translate = locale => (key, params = {}) => { + const value = key.split('.').reduce((tree, part) => tree?.[part], catalogs[locale]); + assert.equal(typeof value, 'string', key); + return value.replace(/\{(\w+)\}/g, (m,k) => params[k] ?? m); +}; +const library = {exports: {}}; +vm.runInNewContext(compile(fs.readFileSync('AppImage/lib/pbs-messages.ts','utf8')), library); +const messages = library.exports; +const failure = code => Object.assign(new Error('RAW DIAGNOSTIC'), {body: {error:'RAW DIAGNOSTIC',code}}); +const flatten = n => !n || typeof n === 'boolean' ? [] : Array.isArray(n) ? n.flatMap(flatten) + : typeof n === 'object' ? n.type === 'Dialog' && !n.props.open ? [] : [n,...flatten(n.props?.children)] : [n]; +const text = n => flatten(n).filter(n => typeof n === 'string' || typeof n === 'number').join(''); + +function fixture(locale='sk', options={}) { + let cursor=0, slots=[], effects=[], tree, apiError, reloadError; + const calls=[], confirmations=[], reloads=[]; + const servers={local_available:false,servers:[{id:'saved',name:'Saved PBS',url:'https://saved.invalid:8007',token_id:'audit@pbs!monitor',has_token:true,fingerprint:''}],detected:[]}; + const status={name:'Fixture',version:'4.2.5',fetched_at:1700000000,services:{'proxmox-backup':'inactive'},datastores:[],tasks:[],sync:[],verify:[],prune:[],errors:{}}; + const t=translate(locale), loaded={}; + const jsx=(type,props) => typeof type === 'function' ? type(props) : ({type,props}); + const react={useState(initial){const i=cursor++;if(!(i in slots))slots[i]=typeof initial==='function'?initial():initial;return [slots[i],value=>{slots[i]=typeof value==='function'?value(slots[i]):value;}];}, + useEffect(fn,deps){const i=cursor++, old=slots[i];if(!old||deps.some((d,j)=>d!==old[j]))effects.push(fn);slots[i]=deps;}}; + function requireModule(name) { + if(name==='react')return react; + if(name==='react/jsx-runtime')return {jsx,jsxs:jsx,Fragment:'Fragment'}; + if(name==='swr')return {default:key=>({data:key?.startsWith('/api/pbs/status')?status:key?servers:undefined,error:options.loadError, + isLoading:false,mutate:async()=>{reloads.push(key);if(reloadError)throw reloadError;}})}; + if(name.includes('i18n/provider'))return {useT:()=>t,useI18n:()=>({language:locale})}; + if(name.includes('pbs-messages'))return messages; + if(name.includes('api-config'))return {fetchApi:async(path,opts)=>{calls.push({path,...opts});if(apiError)throw apiError;return {id:'new',success:true};}}; + if(name.endsWith('pbs-problem-message'))return load('AppImage/components/pbs-problem-message.tsx'); + if(name.endsWith('pbs-datastore-usage'))return load('AppImage/components/pbs-datastore-usage.tsx'); + if(name.includes('lib/utils'))return {formatStorage:n=>String(n)+' GB'}; + return new Proxy({}, {get:(_,key)=>key}); + } + function load(path) {if(loaded[path])return loaded[path];const e={};loaded[path]=e; + vm.runInNewContext(compile(fs.readFileSync(path,'utf8')), {exports:e,require:requireModule,Date,window:{confirm:message=>{confirmations.push(message);return options.confirm===true;}}});return e;} + const panel=load('AppImage/components/pbs-panel.tsx').PBSPanel; + return {t,servers,status,calls,confirmations,reloads,jsx,requireModule, + render(props){cursor=0;effects=[];tree=panel(props);effects.forEach(fn=>fn());return tree;}, + nodes:()=>flatten(tree),text:()=>text(tree), + button(label){const n=flatten(tree).find(n=>n.type==='Button'&&text(n)===label);assert.ok(n,label);return n;}, + input(field){const n=flatten(tree).find(n=>n.type==='Input'&&n.props.id==='pbs-'+field);assert.ok(n,field);return n;}, + select(label){const n=flatten(tree).find(n=>n.type==='Select'&&flatten(n).some(n=>n.props?.['aria-label']===label));assert.ok(n,label);return n;}, + fail:e=>{apiError=e;},failReload:e=>{reloadError=e;},load, + }; +} + +function overview(locale, sourcePath='AppImage/components/system-overview.tsx') { + const source=fs.readFileSync(sourcePath,'utf8'), ast=ts.createSourceFile('overview.tsx',source,ts.ScriptTarget.Latest,true,ts.ScriptKind.TSX), branches=[]; + function visit(n){if(ts.isConditionalExpression(n)&&n.condition.getText(ast)==='isPbs'&&n.whenTrue.getText(ast).includes('pbs.datastores'))branches.push(n.whenTrue.getText(ast));ts.forEachChild(n,visit);}visit(ast); + assert.equal(branches.length,2,'overview summary and storage details both consume PBS'); + const f=fixture(locale); + for(const branch of branches) { + for(const state of ['partial','network','empty','loading']) { + const pbsStatus=state==='loading'||state==='network'?undefined:{datastores:state==='empty'?[]:null,errors:{datastores:'RAW DIAGNOSTIC'},error_details:{datastores:{code:'pbs_token_rejected'}}}; + const context={isPbs:true,pbsStatus,pbsError:state==='network'?failure('pbs_connect_failed'):undefined,datastores:pbsStatus?.datastores, + t:f.t,require:f.requireModule,pbsDiagnostic:messages.pbsDiagnostic,PBSProblemMessage:f.load('AppImage/components/pbs-problem-message.tsx').PBSProblemMessage, + DatastoreUsageRow:f.load('AppImage/components/pbs-datastore-usage.tsx').DatastoreUsageRow}; + for(const name of ['Card','CardHeader','CardTitle','CardContent','Database','Badge'])context[name]=name; + vm.runInNewContext(compile('exports.result=('+branch+');'),{...context,exports:context}); + const rendered=text(context.result); + if(state==='partial') {assert.ok(rendered.includes(f.t('pbs.errors.tokenRejected')));assert.ok(!rendered.includes(f.t('pbs.loading')));assert.ok(!flatten(context.result).some(n=>n.props?.className?.includes('animate-pulse')));} + if(state==='network')assert.ok(rendered.includes(f.t('pbs.errors.connectFailed'))); + if(state==='empty')assert.ok(rendered.includes(f.t('pbs.noDatastores'))); + } + } +} + +async function main() { + const backend=fs.readFileSync('AppImage/scripts/pbs_manager.py','utf8')+fs.readFileSync('AppImage/scripts/flask_pbs_routes.py','utf8'); + const codes=new Set([...backend.matchAll(/(?:code|rejected_code)="(pbs_\w+)"/g)].map(m=>m[1])); + for(const locale of ['en','sk']) { + const t=translate(locale); + for(const code of codes) { + if(code==='pbs_unknown')continue; + const err=failure(code);err.body.params={count:32}; + assert.notEqual(messages.pbsErrorText(err,t),t('pbs.loadFailed'),code); + assert.ok(!messages.pbsErrorText(err,t).includes('RAW')); + } + for(const err of [null,undefined,'raw',new Error('raw'),{body:null},{code:'constructor'},{code:'__proto__'},{code:'future'}, + ...[null,0,-1,1.2,'32',Infinity,NaN,2**54].map(count=>({code:'pbs_connection_limit',params:{count}}))]) { + assert.equal(messages.pbsErrorText(err,t),t('pbs.loadFailed')); + } + assert.equal(messages.pbsResultText('OK',t),t('pbs.states.ok')); + assert.equal(messages.pbsResultText('WARNINGS: 2',t),t('pbs.states.warnings',{count:2})); + assert.equal(messages.pbsResultText(undefined,t),t('pbs.states.unknown')); + assert.equal(messages.pbsResultText('unknown',t),t('pbs.states.unknown')); + assert.equal(messages.pbsResultText('TASK ERROR: raw',t),t('pbs.states.error')); + for(const n of [-1,NaN,Infinity])assert.equal(messages.formatPBSDuration(n,t),'—'); + for(const [n,label] of [[0,'0 s'],[59,'59 s'],[60,'1 min 0 s'],[3599,'59 min 59 s'],[3600,'1 h 0 min'],[86399,'23 h 59 min'],[86400,'1 d 0 h']])assert.equal(messages.formatPBSDuration(n,t),label); + assert.equal(messages.pbsTaskType('garbage_collection',t),t('pbs.taskTypes.garbageCollection')); + assert.equal(messages.pbsTaskType('future',t),t('pbs.taskTypes.other',{type:'future'})); + assert.equal(messages.pbsServiceState('constructor',t),t('pbs.serviceStates.unknown')); + const unknown=fixture(locale);unknown.status.tasks=[{upid:'unknown',endtime:1700000000,status:'unknown'}];unknown.status.sync=[{id:'unknown','last-run-state':'unknown'}]; + unknown.render();unknown.render(); + const unknownBadges=unknown.nodes().filter(n=>n.type==='Badge'&&text(n)===t('pbs.states.unknown')); + assert.equal(unknownBadges.length,2);assert.ok(unknownBadges.every(n=>!n.props.className.includes('red-'))); + const f=fixture(locale);f.render();f.render();f.button(t('pbs.manage')).props.onClick();f.render(); + f.input('name').props.onChange({target:{value:'Draft'}});f.render(); + f.input('password').props.onChange({target:{value:'fake-one-time-password'}});f.render(); + f.button(t('pbs.cancel')).props.onClick();f.render();assert.equal(f.calls.length,0); + f.button(t('pbs.manage')).props.onClick();f.render();assert.equal(f.input('password').props.value,''); + f.input('name').props.onChange({target:{value:'Draft'}});f.render();f.input('url').props.onChange({target:{value:'http://bad.invalid'}});f.render(); + f.fail(failure('pbs_invalid_url'));await f.button(t('pbs.save')).props.onClick();f.render(); + assert.ok(f.text().includes(t('pbs.errors.invalidUrl')));assert.equal(f.input('url').props.value,'http://bad.invalid'); + assert.equal(f.calls[0].path,'/api/pbs/servers/login');assert.ok(!Object.hasOwn(JSON.parse(f.calls[0].body),'token_secret')); + f.button(t('pbs.methodToken')).props.onClick();f.render();await f.button(t('pbs.save')).props.onClick();f.render(); + assert.equal(f.calls[1].path,'/api/pbs/servers');assert.ok(!Object.hasOwn(JSON.parse(f.calls[1].body),'password')); + f.select(t('pbs.editConnection')).props.onValueChange('saved');f.render();await f.button(t('pbs.remove')).props.onClick();f.render(); + assert.deepEqual(f.confirmations,[t('pbs.removeConfirm')]);assert.equal(f.calls.length,2,'Declined remove has no DELETE'); + const remove=fixture(locale,{confirm:true});remove.render();remove.render();remove.button(t('pbs.manage')).props.onClick();remove.render();remove.select(t('pbs.editConnection')).props.onValueChange('saved');remove.render(); + await remove.button(t('pbs.remove')).props.onClick();remove.render();assert.equal(remove.calls.length,1);assert.equal(remove.calls[0].method,'DELETE'); + const failedRemove=fixture(locale,{confirm:true});failedRemove.render();failedRemove.render();failedRemove.button(t('pbs.manage')).props.onClick();failedRemove.render();failedRemove.select(t('pbs.editConnection')).props.onValueChange('saved');failedRemove.render();failedRemove.fail(new Error('network failure')); + await failedRemove.button(t('pbs.remove')).props.onClick();failedRemove.render();assert.ok(failedRemove.text().includes(t('pbs.removeFailed')));assert.equal(failedRemove.calls.length,1); + const failedReload=fixture(locale);failedReload.render();failedReload.render();failedReload.button(t('pbs.manage')).props.onClick();failedReload.render();failedReload.failReload(new Error('raw reload')); + for(const [field,value] of Object.entries({name:'Valid',url:'https://fixture.invalid',username:'root@pam',password:'fake'})){failedReload.input(field).props.onChange({target:{value}});failedReload.render();} + assert.equal(failedReload.button(t('pbs.save')).props.disabled,false); + await failedReload.button(t('pbs.save')).props.onClick();failedReload.render();assert.equal(failedReload.calls.length,1,'No automatic mutation retry after refresh fails');assert.ok(failedReload.text().includes(t('pbs.savedRefreshFailed'))); + const removeReload=fixture(locale,{confirm:true});removeReload.render();removeReload.render();removeReload.button(t('pbs.manage')).props.onClick();removeReload.render();removeReload.select(t('pbs.editConnection')).props.onValueChange('saved');removeReload.render();removeReload.failReload(new Error('network')); + await removeReload.button(t('pbs.remove')).props.onClick();removeReload.render();assert.equal(removeReload.calls.length,1);assert.ok(removeReload.text().includes(t('pbs.removedRefreshFailed'))); + const store=f.load('AppImage/components/pbs-datastore-usage.tsx').PBSDatastoreCard({store:{name:'Offline',total:null,used:null,error:'RAW DIAGNOSTIC',error_code:'pbs_datastore_offline'}}); + assert.ok(text(store).includes(t('pbs.errors.datastoreOffline')));assert.ok(!text(store).includes('0 GB')); + overview(locale); + } + console.log('PASS: all structured PBS error codes, EN/SK/unknown diagnostics, forms/login/token/remove/cancel, no automatic retry, task/service/duration states and both real overview branches'); +} +main().catch(error=>{console.error(error);process.exitCode=1}); +module.exports={fixture,overview}; From f9e36f0b0aed236a2dd1fa204e1e26cd2823106d Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 08:13:34 +0000 Subject: [PATCH 03/27] fix(i18n): align PBS fields and omit duplicate English errors --- AppImage/components/pbs-panel.tsx | 11 ++++++++--- AppImage/components/pbs-problem-message.tsx | 7 ++++--- AppImage/lib/pbs-messages.ts | 20 ++++++++++++++------ AppImage/messages/en/common.json | 15 ++++++++++----- AppImage/messages/sk/common.json | 15 ++++++++++----- tests/test_pbs_i18n.cjs | 20 ++++++++++++++++++++ 6 files changed, 66 insertions(+), 22 deletions(-) diff --git a/AppImage/components/pbs-panel.tsx b/AppImage/components/pbs-panel.tsx index 5dff026e..b18a3e12 100644 --- a/AppImage/components/pbs-panel.tsx +++ b/AppImage/components/pbs-panel.tsx @@ -208,11 +208,16 @@ export function PBSPanel({ localOnly = false, view = "all" }: { localOnly?: bool
{(["name", "url", ...(method === "password" && !form.id ? ["username", "password"] : ["token_id", "token_secret"]), "fingerprint"] as const).map(field => { const secret = field === "token_secret" || field === "password" - return
- + const help = field === "url" ? "pbs.urlHelp" : field === "username" ? "pbs.usernameHelp" + : field === "token_id" ? "pbs.tokenIdHelp" : field === "token_secret" + ? form.id ? "pbs.tokenSecretKeepHelp" : "pbs.tokenSecretHelp" : undefined + return
+ setForm({ ...form, [field]: event.target.value })} /> + {help &&

{t(help)}

}
})}
@@ -324,7 +329,7 @@ function PBSResultBadge({ status, running = false, tone }: { status?: string; ru const badge = {running ? t("pbs.running") : pbsResultText(status, t)} - if (running || !status || /^ok$/i.test(status)) return badge + if (running || !status || status === "unknown" || /^ok$/i.test(status) || /^WARNINGS:\s*\d+\s*$/i.test(status)) return badge return
{badge}
{status}
diff --git a/AppImage/components/pbs-problem-message.tsx b/AppImage/components/pbs-problem-message.tsx index 459701c4..f8286da9 100644 --- a/AppImage/components/pbs-problem-message.tsx +++ b/AppImage/components/pbs-problem-message.tsx @@ -1,16 +1,17 @@ "use client" -import { pbsErrorText } from "../lib/pbs-messages" +import { pbsErrorText, pbsIsKnownError } from "../lib/pbs-messages" import { useT } from "../lib/i18n/provider" -// Keep PBS's original diagnostic available without making it the UI message. +// Known application messages are already explained in the active language. +// Preserve unrecognized external diagnostics as escaped technical details. export function PBSProblemMessage({ problem, raw, fallback, className = "" }: { problem?: unknown; raw?: string; fallback?: string; className?: string }) { const t = useT() return

{pbsErrorText(problem, t, fallback)}

- {raw &&
+ {raw && !pbsIsKnownError(problem) &&
{t("pbs.technicalDetails")}
{raw}
} diff --git a/AppImage/lib/pbs-messages.ts b/AppImage/lib/pbs-messages.ts index 6ce6faa3..6314d317 100644 --- a/AppImage/lib/pbs-messages.ts +++ b/AppImage/lib/pbs-messages.ts @@ -20,18 +20,26 @@ const ERROR_KEYS: Record = { pbs_datastore_offline: "pbs.errors.datastoreOffline", pbs_datastore_unavailable: "pbs.errors.datastoreUnavailable", } -// Accept structured codes from fetchApi, partial status or a datastore. Legacy -// English errors remain diagnostics; never interpret arbitrary text as a key. -export function pbsErrorText(error: unknown, t: Translate, fallback = "pbs.loadFailed"): string { +// Accept structured codes, never arbitrary error text as a translation key. +function pbsErrorTranslation(error: unknown): { key: string; params?: { count: number } } | null { const body = error && typeof error === "object" && "body" in error ? error.body : error - if (!body || typeof body !== "object" || !("code" in body) || typeof body.code !== "string") return t(fallback) + if (!body || typeof body !== "object" || !("code" in body) || typeof body.code !== "string") return null if (body.code === "pbs_connection_limit") { const params = "params" in body ? body.params : null const count = params && typeof params === "object" && "count" in params ? params.count : null return typeof count === "number" && Number.isSafeInteger(count) && count > 0 - ? t("pbs.errors.connectionLimit", { count }) : t(fallback) + ? { key: "pbs.errors.connectionLimit", params: { count } } : null } - return Object.prototype.hasOwnProperty.call(ERROR_KEYS, body.code) ? t(ERROR_KEYS[body.code]) : t(fallback) + return Object.prototype.hasOwnProperty.call(ERROR_KEYS, body.code) ? { key: ERROR_KEYS[body.code] } : null +} + +export function pbsIsKnownError(error: unknown): boolean { + return pbsErrorTranslation(error) !== null +} + +export function pbsErrorText(error: unknown, t: Translate, fallback = "pbs.loadFailed"): string { + const translated = pbsErrorTranslation(error) + return translated ? t(translated.key, translated.params) : t(fallback) } export function pbsDiagnostic(error: unknown): string | undefined { diff --git a/AppImage/messages/en/common.json b/AppImage/messages/en/common.json index f4a3b71c..7bc7b31c 100644 --- a/AppImage/messages/en/common.json +++ b/AppImage/messages/en/common.json @@ -14,7 +14,7 @@ "signInWith": "Sign in with", "methodPassword": "User and password", "methodToken": "API token", - "username": "User (user@realm)", + "username": "User", "password": "Password", "passwordHelp": "The password is used once to create a read-only API token (Audit role) for this Monitor. Only that token is saved, never the password. Enable Monitor authentication first.", "cancel": "Cancel", @@ -27,10 +27,15 @@ "editConnection": "Connection to edit", "add": "Add a new connection", "name": "Name", - "url": "HTTPS URL (include :8007 for the native PBS port)", - "token_id": "API token ID (user@realm!token)", - "token_secret": "API token secret (leave empty to keep the stored secret)", - "fingerprint": "SHA256 certificate fingerprint (optional)", + "url": "HTTPS address", + "urlHelp": "Include :8007 for the default PBS port, for example https://pbs.example:8007.", + "usernameHelp": "Use user@realm, for example root@pam.", + "tokenIdHelp": "Use user@realm!token, for example monitor@pbs!proxmenux.", + "tokenSecretHelp": "Enter the secret of this API token.", + "tokenSecretKeepHelp": "Leave empty to keep the saved token secret.", + "token_id": "API token ID", + "token_secret": "API token secret", + "fingerprint": "SHA256 fingerprint (optional)", "tlsHelp": "Leave the fingerprint empty for normal CA and hostname verification. For a self-signed certificate, copy its SHA256 fingerprint from the PBS console or another trusted channel. The pin is checked before sending credentials.", "saved": "PBS connection verified and saved.", "saveFailed": "Could not save the PBS connection.", diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index 801b456e..418bf19b 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -7310,7 +7310,7 @@ "signInWith": "Prihlásiť sa pomocou", "methodPassword": "Používateľ a heslo", "methodToken": "API token", - "username": "Používateľ (user@realm)", + "username": "Používateľ", "password": "Heslo", "passwordHelp": "Heslo sa použije iba raz na vytvorenie API tokenu iba na čítanie s rolou Audit pre tento Monitor. Uloží sa iba token, heslo sa neukladá. Najprv zapni overovanie v Monitore.", "cancel": "Zrušiť", @@ -7323,10 +7323,15 @@ "editConnection": "Pripojenie na úpravu", "add": "Pridať nové pripojenie", "name": "Názov", - "url": "Adresa HTTPS (pre štandardný port PBS uveď :8007)", - "token_id": "ID API tokenu (user@realm!token)", - "token_secret": "Tajný kľúč API tokenu (prázdne pole zachová uložený kľúč)", - "fingerprint": "Odtlačok SHA256 certifikátu (voliteľný)", + "url": "Adresa HTTPS", + "urlHelp": "Štandardný port PBS je 8007, napríklad https://pbs.example:8007.", + "usernameHelp": "Použi meno a overovaciu oblasť oddelené znakom @, napríklad root@pam.", + "tokenIdHelp": "Použi formát meno@oblasť!token, napríklad monitor@pbs!proxmenux.", + "tokenSecretHelp": "Zadaj tajný kľúč k tomuto API tokenu.", + "tokenSecretKeepHelp": "Prázdne pole zachová uložený tajný kľúč tokenu.", + "token_id": "ID API tokenu", + "token_secret": "Tajný kľúč API tokenu", + "fingerprint": "Odtlačok SHA256 (voliteľný)", "tlsHelp": "Pri bežnom overovaní certifikačnej autority a názvu hostiteľa nechaj odtlačok prázdny. Pri certifikáte s vlastným podpisom skopíruj odtlačok SHA256 z konzoly PBS alebo iného dôveryhodného zdroja. Kontroluje sa ešte pred odoslaním prihlasovacích údajov.", "saved": "Pripojenie PBS bolo overené a uložené.", "saveFailed": "Nepodarilo sa uložiť pripojenie PBS.", diff --git a/tests/test_pbs_i18n.cjs b/tests/test_pbs_i18n.cjs index 1712b9d8..93d92605 100644 --- a/tests/test_pbs_i18n.cjs +++ b/tests/test_pbs_i18n.cjs @@ -97,11 +97,28 @@ async function main() { assert.equal(messages.pbsTaskType('garbage_collection',t),t('pbs.taskTypes.garbageCollection')); assert.equal(messages.pbsTaskType('future',t),t('pbs.taskTypes.other',{type:'future'})); assert.equal(messages.pbsServiceState('constructor',t),t('pbs.serviceStates.unknown')); + assert.equal(t('pbs.url'),locale==='sk'?'Adresa HTTPS':'HTTPS address'); + const diagnosticFixture=fixture(locale),problem=diagnosticFixture.load('AppImage/components/pbs-problem-message.tsx').PBSProblemMessage; + for(const code of codes){ + if(code==='pbs_unknown')continue; + const err=failure(code);err.body.params={count:32}; + const rendered=problem({problem:err,raw:'RAW DIAGNOSTIC'}); + assert.ok(!text(rendered).includes('RAW DIAGNOSTIC'),'Known '+code+' must not repeat an English application message'); + assert.ok(!flatten(rendered).some(n=>n.type==='details')); + } + for(const code of ['future','__proto__','constructor']){ + const rendered=problem({problem:failure(code),raw:'RAW DIAGNOSTIC'}); + assert.ok(text(rendered).includes('RAW DIAGNOSTIC'));assert.ok(flatten(rendered).some(n=>n.type==='details')); + } const unknown=fixture(locale);unknown.status.tasks=[{upid:'unknown',endtime:1700000000,status:'unknown'}];unknown.status.sync=[{id:'unknown','last-run-state':'unknown'}]; unknown.render();unknown.render(); const unknownBadges=unknown.nodes().filter(n=>n.type==='Badge'&&text(n)===t('pbs.states.unknown')); assert.equal(unknownBadges.length,2);assert.ok(unknownBadges.every(n=>!n.props.className.includes('red-'))); + unknown.status.tasks=[{upid:'warn',endtime:1700000000,status:'WARNINGS: 3'},{upid:'raw',endtime:1700000000,status:'RAW EXTERNAL TASK ERROR'}];unknown.status.sync=[];unknown.render(); + const taskDetails=unknown.nodes().filter(n=>n.type==='details');assert.equal(taskDetails.length,1);assert.ok(text(taskDetails[0]).includes('RAW EXTERNAL TASK ERROR')); const f=fixture(locale);f.render();f.render();f.button(t('pbs.manage')).props.onClick();f.render(); + assert.equal(f.input('url').props['aria-describedby'],'pbs-url-help'); + assert.ok(f.text().includes(t('pbs.urlHelp')));assert.ok(f.text().includes(t('pbs.usernameHelp'))); f.input('name').props.onChange({target:{value:'Draft'}});f.render(); f.input('password').props.onChange({target:{value:'fake-one-time-password'}});f.render(); f.button(t('pbs.cancel')).props.onClick();f.render();assert.equal(f.calls.length,0); @@ -111,8 +128,11 @@ async function main() { assert.ok(f.text().includes(t('pbs.errors.invalidUrl')));assert.equal(f.input('url').props.value,'http://bad.invalid'); assert.equal(f.calls[0].path,'/api/pbs/servers/login');assert.ok(!Object.hasOwn(JSON.parse(f.calls[0].body),'token_secret')); f.button(t('pbs.methodToken')).props.onClick();f.render();await f.button(t('pbs.save')).props.onClick();f.render(); + assert.ok(f.text().includes(t('pbs.tokenSecretHelp')));assert.ok(!f.text().includes(t('pbs.tokenSecretKeepHelp'))); + assert.equal(f.input('token_secret').props['aria-describedby'],'pbs-token_secret-help'); assert.equal(f.calls[1].path,'/api/pbs/servers');assert.ok(!Object.hasOwn(JSON.parse(f.calls[1].body),'password')); f.select(t('pbs.editConnection')).props.onValueChange('saved');f.render();await f.button(t('pbs.remove')).props.onClick();f.render(); + assert.ok(f.text().includes(t('pbs.tokenSecretKeepHelp')));assert.ok(!f.text().includes(t('pbs.tokenSecretHelp'))); assert.deepEqual(f.confirmations,[t('pbs.removeConfirm')]);assert.equal(f.calls.length,2,'Declined remove has no DELETE'); const remove=fixture(locale,{confirm:true});remove.render();remove.render();remove.button(t('pbs.manage')).props.onClick();remove.render();remove.select(t('pbs.editConnection')).props.onValueChange('saved');remove.render(); await remove.button(t('pbs.remove')).props.onClick();remove.render();assert.equal(remove.calls.length,1);assert.equal(remove.calls[0].method,'DELETE'); From 1dc9e92f74fc18b95ddf7b785d96b560ce9b90f7 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 08:35:33 +0000 Subject: [PATCH 04/27] i18n(sk): clarify removal of old backups --- AppImage/messages/sk/common.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index 418bf19b..f7312318 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -4807,7 +4807,7 @@ "keepAll": "Ponechať všetky zálohy", "description": "Predvolená politika uchovávania záloh uložená v Proxmoxe. Zálohovacie úlohy s vlastnou politikou majú pred týmto predvoleným nastavením prednosť.", "refresh": "Obnoviť", - "saved": "Politika uchovávania záloh úložiska bola uložená. Prerezávanie sa nevykonalo.", + "saved": "Pravidlá uchovávania záloh na úložisku boli uložené. Žiadne zálohy sa teraz neodstránili.", "choosePolicy": "Vyberte Ponechať všetky alebo aspoň jeden počet uchovávaných záloh väčší ako nula.", "saveFailed": "Nepodarilo sa uložiť politiku uchovávania záloh úložiska. Ak sa konfigurácia zmenila, obnovte zoznam.", "loadFailed": "Nepodarilo sa načítať politiku uchovávania záloh úložiska.", @@ -5641,7 +5641,7 @@ "nextStep": { "missing": "Nastavte si držanie na týchto pracovných miestach, alebo na sklade, na ktorý píšu. Proxmox berie nastavenie úlohy ako prvý, potom úložisko je, potom uzol predvolený.", "notDeclared": "Tieto práce uchovávajú všetky kópie, ktoré robia. To je zámerná voľba pre niektoré destinácie, ale potvrďte, že cieľ má priestor na rast.", - "onServer": "Tieto úlohy píšu na záložný server, ktorý ich prerezáva podľa vlastných pravidiel. Z tohto uzla sa nedá čítať to, čo drží, takže skontrolujte, či tam nie je." + "onServer": "Tieto úlohy ukladajú zálohy na zálohovací server. Ten odstraňuje staré zálohy podľa vlastných pravidiel. Z tohto uzla sa nedá zistiť, ktoré zálohy uchováva; skontroluj ich priamo na zálohovacom serveri." }, "explanation": "O tom, koľko výtlačkov sa uchováva a ako dlho sa uchováva. Bez nej sa cieľ naplní, kým neprestane prijímať nové kópie, a záloha, ktorú nemožno napísať, je tá, o ktorej sa dozviete v deň, keď ju potrebujete." }, @@ -7356,7 +7356,7 @@ "schedule": "Plán", "sync": "Synchronizácia", "verify": "Overenie", - "prune": "Prerezávanie záloh", + "prune": "Odstraňovanie starých záloh", "overview": "Prehľad", "logs": "Systémové záznamy", "terminal": "Terminál", @@ -7373,7 +7373,7 @@ "estimatedFull": "Odhadované zaplnenie", "estimatedFullUnknown": "Zatiaľ sa nedá odhadnúť", "duration": "Trvanie", - "noJobs": "Nie sú nastavené žiadne plány synchronizácie, overovania ani prerezávania záloh.", + "noJobs": "Nie sú nastavené žiadne plány synchronizácie, overovania ani odstraňovania starých záloh.", "removeFailed": "Nepodarilo sa odstrániť pripojenie PBS.", "savedRefreshFailed": "Pripojenie PBS sa uložilo, ale nepodarilo sa obnoviť zoznam pripojení. Pred ďalšou zmenou obnov stránku.", "removedRefreshFailed": "Pripojenie PBS sa odstránilo, ale nepodarilo sa obnoviť zoznam pripojení. Obnov stránku.", @@ -7415,7 +7415,7 @@ "taskTypes": { "backup": "Zálohovanie", "restore": "Obnova", "verify": "Overovanie", "verifyGroup": "Overovanie skupiny záloh", "verifySnapshot": "Overovanie snímky", - "prune": "Prerezávanie záloh", "garbageCollection": "Čistenie nepoužívaných dát", "sync": "Synchronizácia", + "prune": "Odstraňovanie starých záloh", "garbageCollection": "Čistenie nepoužívaných dát", "sync": "Synchronizácia", "updateCheck": "Kontrola aktualizácií", "terminal": "Terminál", "createDatastore": "Vytvorenie dátového úložiska", "other": "Iná úloha ({type})" }, From aed77ecefe67aa521dc53be799e62b9a6f215e4c Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 08:45:29 +0000 Subject: [PATCH 05/27] i18n: localize host backup application errors --- .github/scripts/run_offline_qualified.py | 1 + AppImage/components/host-backup.tsx | 89 +++++++------- AppImage/lib/host-backup-messages.ts | 141 +++++++++++++++++++++++ AppImage/messages/en/common.json | 118 +++++++++++++++++++ AppImage/messages/sk/common.json | 120 ++++++++++++++++++- tests/test_host_backup_messages.cjs | 103 +++++++++++++++++ 6 files changed, 527 insertions(+), 45 deletions(-) create mode 100644 AppImage/lib/host-backup-messages.ts create mode 100644 tests/test_host_backup_messages.cjs diff --git a/.github/scripts/run_offline_qualified.py b/.github/scripts/run_offline_qualified.py index 9689e2ff..586ff3f5 100644 --- a/.github/scripts/run_offline_qualified.py +++ b/.github/scripts/run_offline_qualified.py @@ -31,6 +31,7 @@ PYTHON_FILES = ( "AppImage/scripts/tests/test_health_persistence_recurrence.py", ) NODE_FILES = ( + "tests/test_host_backup_messages.cjs", "tests/test_pbs_i18n.cjs", "tests/test_monitor_links_i18n.cjs", "tests/lxc_updates/test_local_app_icons_ui.cjs", diff --git a/AppImage/components/host-backup.tsx b/AppImage/components/host-backup.tsx index 07e10b94..30434ff7 100644 --- a/AppImage/components/host-backup.tsx +++ b/AppImage/components/host-backup.tsx @@ -48,6 +48,7 @@ import { import { ScriptTerminalModal } from "./script-terminal-modal" import { RestoreProgressCard } from "./restore-progress-card" import { fetchApi, getApiUrl } from "../lib/api-config" +import { hostBackupErrorText } from "../lib/host-backup-messages" import { fetchTerminalTicket } from "../lib/terminal-ws" import { formatStorage, formatBytes, cancelButtonClass } from "../lib/utils" import { getStorageUsageColor } from "../lib/storage-usage-color" @@ -525,7 +526,7 @@ function PbsKeyfileActions({ pbsRepository }: { pbsRepository?: string }) { await mutateInfo() closeUpload() } catch (e) { - setErr(e instanceof Error ? e.message : String(e)) + setErr(hostBackupErrorText(e, t)) setBusy(false) } } @@ -542,7 +543,7 @@ function PbsKeyfileActions({ pbsRepository }: { pbsRepository?: string }) { await mutateInfo() closeDelete() } catch (e) { - setErr(e instanceof Error ? e.message : String(e)) + setErr(hostBackupErrorText(e, t)) setBusy(false) } } @@ -765,7 +766,7 @@ function KeyfileActionsBar({ setPass1("") setPass2("") } catch (e) { - setErr(e instanceof Error ? e.message : String(e)) + setErr(hostBackupErrorText(e, t)) } finally { setBusy(false) } @@ -956,7 +957,7 @@ export function HostBackup() { mutateJobs() setJobToDelete(null) } catch (e) { - setActionError(t("backup.errors.deleteFailed", { error: `"${id}": ${e instanceof Error ? e.message : String(e)}` })) + setActionError(t("backup.errors.deleteFailed", { error: `"${id}": ${hostBackupErrorText(e, t)}` })) } finally { setBusyJobId(null) } @@ -1334,7 +1335,7 @@ export function HostBackup() {
{t("backup.archives.remoteQueryWarning")}
{remoteArchivesResp.errors.map((e, i) => (
- {e.backend}/{e.repo_name}: {e.error} + {e.backend}/{e.repo_name}: {hostBackupErrorText(e.error, t)}
))}
@@ -1761,7 +1762,7 @@ function InspectModal({ setImportFile(null) setImportPath("") } catch (e) { - setImportError(e instanceof Error ? e.message : String(e)) + setImportError(hostBackupErrorText(e, t)) } finally { setImporting(false) } @@ -1812,7 +1813,7 @@ function InspectModal({ : undefined, }) } catch (e) { - setRestoreError(e instanceof Error ? e.message : String(e)) + setRestoreError(hostBackupErrorText(e, t)) } finally { setRestorePreparing(false) } @@ -1841,7 +1842,7 @@ function InspectModal({ a.click() document.body.removeChild(a) } catch (e) { - setError(t("backup.errors.downloadFailed", { error: e instanceof Error ? e.message : String(e) })) + setError(t("backup.errors.downloadFailed", { error: hostBackupErrorText(e, t) })) } finally { // The download itself runs in the browser's network stack; // we just initiated it. Clear the spinner immediately. @@ -1914,7 +1915,7 @@ function InspectModal({ document.body.removeChild(a) setExportTask(null) } catch (e) { - setError(t("backup.errors.downloadFailed", { error: e instanceof Error ? e.message : String(e) })) + setError(t("backup.errors.downloadFailed", { error: hostBackupErrorText(e, t) })) } finally { setDownloading(false) } @@ -1962,7 +1963,7 @@ function InspectModal({ setShowDeleteArchiveConfirm(false) if (onDeleted) onDeleted(); else onClose() } catch (e) { - setError(t("backup.errors.deleteFailed", { error: e instanceof Error ? e.message : String(e) })) + setError(t("backup.errors.deleteFailed", { error: hostBackupErrorText(e, t) })) } finally { setDeletingArchive(false) } @@ -1989,7 +1990,7 @@ function InspectModal({ ) as NonNullable setInstructions(res) } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setFetchingInstructions(false) } @@ -2021,7 +2022,7 @@ function InspectModal({ ) setReport(res) } catch (e: any) { - setError(e?.message || t("backup.errors.preflightFailed")) + setError(hostBackupErrorText(e, t)) } finally { setRunning(false) } @@ -2164,7 +2165,7 @@ function InspectModal({ — {exportTask.message}
{exportTask.state === "failed" && exportTask.error && ( -
{exportTask.error}
+
{hostBackupErrorText(exportTask.error, t)}
)} {exportTask.state === "completed" && exportTask.size_bytes > 0 && (
{t("backup.archives.packedSizeLabel")} {formatBytes(exportTask.size_bytes)}
@@ -2706,7 +2707,7 @@ function PreflightReportView({ report }: { report: PreflightReport }) {
{c.id} - {c.message} + {hostBackupErrorText(c.message, t)}
) @@ -3326,9 +3327,9 @@ function CreateJobDialog({ } catch (e) { const err = e as Error & { body?: { tool_output?: string; tool_exit_code?: number } } const detail = err.body?.tool_output - ? `${err.message}\n\n${t("backup.errors.proxmoxBackupClientOutput")}:\n${err.body.tool_output}` - : err.message - setError(t("backup.errors.encryptionSetupFailed", { detail: detail || String(e) })) + ? `${hostBackupErrorText(err, t)}\n\n${t("backup.errors.proxmoxBackupClientOutput")}:\n${err.body.tool_output}` + : hostBackupErrorText(err, t) + setError(t("backup.errors.encryptionSetupFailed", { error: detail || hostBackupErrorText(e, t) })) setPbsImportBusy(false) setSubmitting(false) return @@ -3391,7 +3392,7 @@ function CreateJobDialog({ }) onCreated() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setSubmitting(false) } @@ -3764,7 +3765,7 @@ function CreateJobDialog({ ) : (
- {t("backup.validation.invalidLabel")} {calendarPreview.error} + {t("backup.validation.invalidLabel")} {hostBackupErrorText(calendarPreview.error, t)}
) ) : ( @@ -4696,9 +4697,9 @@ function ManualBackupDialog({ } catch (e) { const err = e as Error & { body?: { tool_output?: string; tool_exit_code?: number } } const detail = err.body?.tool_output - ? `${err.message}\n\n${t("backup.errors.proxmoxBackupClientOutput")}:\n${err.body.tool_output}` - : err.message - setError(t("backup.errors.encryptionSetupFailed", { detail: detail || String(e) })) + ? `${hostBackupErrorText(err, t)}\n\n${t("backup.errors.proxmoxBackupClientOutput")}:\n${err.body.tool_output}` + : hostBackupErrorText(err, t) + setError(t("backup.errors.encryptionSetupFailed", { error: detail || hostBackupErrorText(e, t) })) setPbsImportBusy(false) setSubmitting(false) return @@ -4738,7 +4739,7 @@ function ManualBackupDialog({ ) onLaunched(resp.job_id) } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setSubmitting(false) } @@ -5542,7 +5543,7 @@ function DestinationsSection({ await fetchApi(`/api/host-backups/destinations/pbs/${encodeURIComponent(name)}${force ? "?force=true" : ""}`, { method: "DELETE" }) onChanged() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyKey(null) } @@ -5554,7 +5555,7 @@ function DestinationsSection({ await fetchApi(`/api/host-backups/destinations/borg/${encodeURIComponent(name)}${force ? "?force=true" : ""}`, { method: "DELETE" }) onChanged() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyKey(null) } @@ -5571,7 +5572,7 @@ function DestinationsSection({ ) onChanged() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyKey(null) } @@ -5599,7 +5600,7 @@ function DestinationsSection({ }) onChanged() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyKey(null) } @@ -6042,7 +6043,7 @@ function DestinationRow({ {capacity?.error && (

- {t("backup.destinations.capacityUnavailable", { error: capacity.error })} + {t("backup.destinations.capacityUnavailable", { error: hostBackupErrorText(capacity.error, t) })}

)} {!capacity && ( @@ -6341,7 +6342,7 @@ function AddDestinationDialog({ ) setGeneratedKey(resp) } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setGeneratingKey(false) } @@ -6405,7 +6406,7 @@ function AddDestinationDialog({ } onSaved(savedRepo) } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setSubmitting(false) } @@ -6756,7 +6757,7 @@ function UsbPicker({ mutate() if (resp?.mountpoint) onPick(resp.mountpoint) } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyKey(null) } @@ -6784,7 +6785,7 @@ function UsbPicker({ setFormatTyped("") if (resp?.mountpoint) onPick(resp.mountpoint) } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyKey(null) } @@ -7001,7 +7002,7 @@ function ExtraPathsSection() { setNewPath("") mutate() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setSubmitting(false) } @@ -7014,7 +7015,7 @@ function ExtraPathsSection() { await fetchApi(`/api/host-backups/extra-paths?path=${encodeURIComponent(p)}`, { method: "DELETE" }) mutate() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyPath(null) } @@ -7180,7 +7181,7 @@ function UsbDrivesSection() { }) mutate() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyKey(null) } @@ -7198,7 +7199,7 @@ function UsbDrivesSection() { }) mutate() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyKey(null) } @@ -7222,7 +7223,7 @@ function UsbDrivesSection() { setFormatTarget(null) setFormatTyped("") } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusyKey(null) } @@ -7508,7 +7509,7 @@ function JobDetailModal({ setRunning(false) setRunBaseline(null) setRunBaselineLogPath(null) - setActionError(t("backup.errors.runFailed", { error: e instanceof Error ? e.message : String(e) })) + setActionError(t("backup.errors.runFailed", { error: hostBackupErrorText(e, t) })) } } @@ -7522,7 +7523,7 @@ function JobDetailModal({ await refetch() onChanged() } catch (e) { - setActionError(t("backup.errors.toggleFailed", { error: e instanceof Error ? e.message : String(e) })) + setActionError(t("backup.errors.toggleFailed", { error: hostBackupErrorText(e, t) })) } finally { setBusy("") } @@ -8155,7 +8156,7 @@ function PbsKeyfileRecoveryDialog({ }) onRecovered() } catch (e) { - setError(e instanceof Error ? e.message : String(e)) + setError(hostBackupErrorText(e, t)) } finally { setBusy(false) } @@ -8342,7 +8343,7 @@ function ArchiveContentsModal({ body: JSON.stringify(body), }) .then((r: any) => setData(r)) - .catch((e) => setError(e instanceof Error ? e.message : String(e))) + .catch((e) => setError(hostBackupErrorText(e, t))) .finally(() => setLoading(false)) }, [open, source, repo_name, snapshot, path]) @@ -8571,7 +8572,7 @@ function PlanSummary({ plan }: { plan: any }) {
{t("backup.plan.blockers")}
    {blockers.map((b, i) => ( -
  • {typeof b === "string" ? b : (b.message || JSON.stringify(b))}
  • +
  • {hostBackupErrorText(typeof b === "string" ? b : (b.message || JSON.stringify(b)), t)}
  • ))}
@@ -8581,7 +8582,7 @@ function PlanSummary({ plan }: { plan: any }) {
{t("backup.plan.warnings")}
    {warnings.map((w, i) => ( -
  • {typeof w === "string" ? w : (w.message || JSON.stringify(w))}
  • +
  • {hostBackupErrorText(typeof w === "string" ? w : (w.message || JSON.stringify(w)), t)}
  • ))}
@@ -8611,7 +8612,7 @@ function StorageSection({ storage }: { storage: any }) {
{e.name || e.storage || `entry ${i}`} {e.type && {e.type}} - {e.message &&
{e.message}
} + {e.message &&
{hostBackupErrorText(e.message, t)}
}
) diff --git a/AppImage/lib/host-backup-messages.ts b/AppImage/lib/host-backup-messages.ts new file mode 100644 index 00000000..6129df23 --- /dev/null +++ b/AppImage/lib/host-backup-messages.ts @@ -0,0 +1,141 @@ +// Legacy HostBackup API messages are an English wire contract shared with CLI callers. +// Match complete, source-verified messages only; never derive a translation key from input. +// This presentation-only adapter keeps unknown tool diagnostics and parameter text intact. +type Translate = (key: string, params?: Record) => string + +const fixed = new Map([ + ["path must be absolute", "backup.apiErrors.absolutePath"], + ["this path is already configured", "backup.apiErrors.pathConfigured"], + ["path is required", "backup.apiErrors.pathRequired"], + ["path query parameter is required", "backup.apiErrors.pathQuery"], + ["path not found", "backup.apiErrors.pathNotFound"], + ["invalid job id", "backup.apiErrors.invalidJobId"], + ["job not found", "backup.apiErrors.jobNotFound"], + ["runner script not installed", "backup.apiErrors.runnerMissing"], + ["no restore state to dismiss", "backup.apiErrors.noRestore"], + ["invalid file name", "backup.apiErrors.invalidFile"], + ["not found", "backup.apiErrors.notFound"], + ["log path outside allowed directory", "backup.apiErrors.logOutside"], + ["targets must be a list", "backup.apiErrors.targetsList"], + ["empty expression", "backup.apiErrors.emptyExpression"], + ["invalid id (use letters, digits, _ or -)", "backup.apiErrors.invalidId"], + ["backend must be pbs, local or borg", "backup.apiErrors.backend"], + ["backend must be pbs or borg", "backup.apiErrors.remoteBackend"], + ["backend must be 'pbs' or 'borg'", "backup.apiErrors.remoteBackendQuoted"], + ["profile_mode must be default or custom", "backup.apiErrors.profileMode"], + ["borg cannot be attached (PVE vzdump only writes to pbs/local storages)", "backup.apiErrors.borgAttached"], + ["borg is not a valid backend for attached jobs (PVE vzdump only writes to pbs/local storages)", "backup.apiErrors.borgAttachedLong"], + ["pve_parent_job_id and pve_storage are required for attached", "backup.apiErrors.parentRequired"], + ["paths must be a list of absolute paths", "backup.apiErrors.absolutePaths"], + ["custom profile needs at least one path", "backup.apiErrors.customPath"], + ["could not resolve default profile paths", "backup.apiErrors.defaultPaths"], + ["retention must be an object", "backup.apiErrors.retentionObject"], + ["pbs_repository is required", "backup.apiErrors.pbsRepository"], + ["pbs_encrypt_mode must be 'none', 'new' or 'existing'", "backup.apiErrors.pbsEncryption"], + ["failed to create PBS encryption keyfile", "backup.apiErrors.pbsKeyFailed"], + ["failed to create PBS encryption keyfile (is proxmox-backup-client installed?)", "backup.apiErrors.pbsClientMissing"], + ["borg_repo is required", "backup.apiErrors.borgRepository"], + ["borg_encrypt_mode must be one of: none, repokey, keyfile, authenticated", "backup.apiErrors.borgEncryption"], + ["encrypt_mode must be one of: none, repokey, keyfile, authenticated", "backup.apiErrors.encryption"], + ["borg_passphrase is required when encryption is enabled (save it on the destination or pass one in the request)", "backup.apiErrors.borgPassphrase"], + ["passphrase is required when encryption is enabled", "backup.apiErrors.passphrase"], + ["password is required", "backup.apiErrors.password"], + ["confirm_device must match device exactly (typo / safety check)", "backup.apiErrors.deviceConfirmation"], + ["invalid device path", "backup.apiErrors.invalidDevice"], + ["absolute mountpoint required", "backup.apiErrors.absoluteMountpoint"], + ["archive layout not recognized — no rootfs/ found", "backup.apiErrors.archiveLayout"], + ["archive not found", "backup.apiErrors.archiveMissing"], + ["archive path is not a regular file", "backup.apiErrors.archiveNotFile"], + ["invalid name", "backup.apiErrors.invalidName"], + ["invalid staging_path", "backup.apiErrors.invalidStaging"], + ["job name cannot be changed (delete + recreate to rename)", "backup.apiErrors.jobRename"], + ["key_path must be absolute", "backup.apiErrors.absoluteKey"], + ["mode must be \"local\" or \"ssh\"", "backup.apiErrors.mode"], + ["name is required (letters, digits, _ or -)", "backup.apiErrors.nameRequired"], + ["no log available for this job yet", "backup.apiErrors.noLog"], + ["no manual PBS configs file", "backup.apiErrors.noPbsConfig"], + ["output file missing", "backup.apiErrors.outputMissing"], + ["parser output was not valid JSON", "backup.apiErrors.parserJson"], + ["run_restore output was not valid JSON", "backup.apiErrors.restoreJson"], + ["repo is required", "backup.apiErrors.repoRequired"], + ["repo_name and snapshot are required", "backup.apiErrors.repoSnapshot"], + ["repo_name and snapshot are required for borg", "backup.apiErrors.borgRepoSnapshot"], + ["repo_name and snapshot are required for pbs", "backup.apiErrors.pbsRepoSnapshot"], + ["repo_name is required", "backup.apiErrors.repoName"], + ["restore tooling not installed on this host", "backup.apiErrors.restoreMissing"], + ["server and datastore are required", "backup.apiErrors.serverDatastore"], + ["snapshot is required", "backup.apiErrors.snapshotRequired"], + ["source must be pbs, borg, or local", "backup.apiErrors.source"], + ["ssh_port must be an integer between 1 and 65535", "backup.apiErrors.sshPort"], + ["ssh_user, ssh_host and ssh_remote_path are required for ssh mode", "backup.apiErrors.sshRequired"], + ["target has no repository path", "backup.apiErrors.repositoryPath"], + ["task not found or expired", "backup.apiErrors.taskExpired"], + ["invalid expression", "backup.apiErrors.invalidExpression"], + ["no report emitted", "backup.apiErrors.noReport"], + ["parse_manifest exited non-zero", "backup.apiErrors.parseManifestFailed"], +]) + +const parameterized: ReadonlyArray = [ + [new RegExp("^job already exists: ([\\s\\S]+)$"), "backup.apiErrors.jobExists"], + [new RegExp("^path does not exist on this host: ([\\s\\S]+)$"), "backup.apiErrors.pathMissing"], + [new RegExp("^device does not exist: ([\\s\\S]+)$"), "backup.apiErrors.deviceMissing"], + [new RegExp("^not a mountpoint: ([\\s\\S]+)$"), "backup.apiErrors.mountpoint"], + [new RegExp("^local archive not found: ([\\s\\S]+)$"), "backup.apiErrors.localArchiveMissing"], + [new RegExp("^Borg destination '([\\s\\S]+)' not found$"), "backup.apiErrors.borgDestination"], + [new RegExp("^PBS destination '([\\s\\S]+)' not found$"), "backup.apiErrors.pbsDestination"], + [new RegExp("^no password available for PBS repo '([\\s\\S]+)'$"), "backup.apiErrors.pbsPassword"], + [new RegExp("^Borg repo \"([\\s\\S]+)\" not configured$"), "backup.apiErrors.borgRepoMissing"], + [new RegExp("^PBS repo \"([\\s\\S]+)\" not configured$"), "backup.apiErrors.pbsRepoMissing"], + [new RegExp("^Borg target not found: ([\\s\\S]+)$"), "backup.apiErrors.borgTargetMissing"], + [new RegExp("^PBS repo not found: ([\\s\\S]+)$"), "backup.apiErrors.pbsTargetMissing"], + [new RegExp("^PVE vzdump job not found: ([\\s\\S]+)$"), "backup.apiErrors.pveJobMissing"], + [new RegExp("^cannot persist state: ([\\s\\S]+)$"), "backup.apiErrors.statePersist"], + [new RegExp("^cannot prepare ssh dir: ([\\s\\S]+)$"), "backup.apiErrors.sshDirectory"], + [new RegExp("^cannot read log: ([\\s\\S]+)$"), "backup.apiErrors.logRead"], + [new RegExp("^could not update \\.env: ([\\s\\S]+)$"), "backup.apiErrors.envUpdate"], + [new RegExp("^failed to persist PBS config: ([\\s\\S]+)$"), "backup.apiErrors.pbsConfigWrite"], + [new RegExp("^failed to persist: ([\\s\\S]+)$"), "backup.apiErrors.persist"], + [new RegExp("^failed to remove archive: ([\\s\\S]+)$"), "backup.apiErrors.archiveRemove"], + [new RegExp("^failed to start: ([\\s\\S]+)$"), "backup.apiErrors.start"], + [new RegExp("^failed to update borg-targets\\.txt: ([\\s\\S]+)$"), "backup.apiErrors.borgTargetsUpdate"], + [new RegExp("^failed to update pbs-manual-configs\\.txt: ([\\s\\S]+)$"), "backup.apiErrors.pbsTargetsUpdate"], + [new RegExp("^failed to update systemd units: ([\\s\\S]+)$"), "backup.apiErrors.unitsUpdate"], + [new RegExp("^failed to write borg-targets\\.txt: ([\\s\\S]+)$"), "backup.apiErrors.borgTargetsWrite"], + [new RegExp("^failed to write job files: ([\\s\\S]+)$"), "backup.apiErrors.jobWrite"], + [new RegExp("^failed to write local-target\\.conf: ([\\s\\S]+)$"), "backup.apiErrors.localTargetWrite"], + [new RegExp("^failed to write systemd units: ([\\s\\S]+)$"), "backup.apiErrors.unitsWrite"], + [new RegExp("^format failed: ([\\s\\S]+)$"), "backup.apiErrors.format"], + [new RegExp("^invalid on_calendar: ([\\s\\S]+)$"), "backup.apiErrors.calendar"], + [new RegExp("^mount failed: ([\\s\\S]+)$"), "backup.apiErrors.mount"], + [new RegExp("^parser invocation failed: ([\\s\\S]+)$"), "backup.apiErrors.parserInvocation"], + [new RegExp("^preflight invocation failed: ([\\s\\S]+)$"), "backup.apiErrors.preflightInvocation"], + [new RegExp("^public key not found after generation: ([\\s\\S]+)$"), "backup.apiErrors.publicKeyMissing"], + [new RegExp("^retention\\.([\\s\\S]+) must be an integer$"), "backup.apiErrors.retentionInteger"], + [new RegExp("^retention\\.([\\s\\S]+) must be non-negative$"), "backup.apiErrors.retentionNegative"], + [new RegExp("^ssh-keygen failed: ([\\s\\S]+)$"), "backup.apiErrors.keygen"], + [new RegExp("^systemctl failed: ([\\s\\S]+)$"), "backup.apiErrors.systemctl"], + [new RegExp("^umount failed: ([\\s\\S]+)$"), "backup.apiErrors.unmount"], + [new RegExp("^task is in state \"([\\s\\S]+)\", not completed$"), "backup.apiErrors.taskIncomplete"], + [new RegExp("^unknown kind: ([\\s\\S]+)$"), "backup.apiErrors.unknownKind"], + [new RegExp("^unknown mode \"([\\s\\S]+)\"$"), "backup.apiErrors.unknownMode"], + [new RegExp("^([\\s\\S]+) is the built-in default and cannot be removed$"), "backup.apiErrors.defaultRemove"], + [new RegExp("^([\\s\\S]+) is the built-in default; no need to add it$"), "backup.apiErrors.defaultAdd"], + [new RegExp("^pbs_encrypt_mode='existing' but no keyfile is installed at ([\\s\\S]+) — import one first with POST /api/host-backups/pbs-encryption/import$"), "backup.apiErrors.keyImport"], + [new RegExp("^PVE job storage mismatch \\(job uses ([\\s\\S]+), payload says ([\\s\\S]+)\\)$"), "backup.apiErrors.storageMismatch"], +] + +export function hostBackupErrorText(problem: unknown, t: Translate): string { + const message = typeof problem === "string" ? problem + : problem && typeof problem === "object" && "message" in problem && typeof problem.message === "string" + ? problem.message : "" + if (!message) return t("backup.apiErrors.requestFailed") + const key = fixed.get(message) + if (key) return t(key) + // Keep unusually large tool output verbatim, without running pattern matching over it. + if (message.length > 16384) return message + for (const [pattern, patternKey] of parameterized) { + const match = pattern.exec(message) + if (match) return t(patternKey, { value: match[1], other: match[2] ?? "" }) + } + return message +} diff --git a/AppImage/messages/en/common.json b/AppImage/messages/en/common.json index 7bc7b31c..cb13efab 100644 --- a/AppImage/messages/en/common.json +++ b/AppImage/messages/en/common.json @@ -4368,6 +4368,124 @@ "unrecoverable": "Without the key, encrypted backups cannot be restored.", "usesSharedKeyfileAt": "Uses shared keyfile at" }, + "apiErrors": { + "requestFailed": "The request failed.", + "absolutePath": "path must be absolute", + "pathConfigured": "this path is already configured", + "pathRequired": "path is required", + "pathQuery": "path query parameter is required", + "pathNotFound": "path not found", + "invalidJobId": "invalid job id", + "jobNotFound": "job not found", + "runnerMissing": "runner script not installed", + "noRestore": "no restore state to dismiss", + "invalidFile": "invalid file name", + "notFound": "not found", + "logOutside": "log path outside allowed directory", + "targetsList": "targets must be a list", + "emptyExpression": "empty expression", + "invalidId": "invalid id (use letters, digits, _ or -)", + "backend": "backend must be pbs, local or borg", + "remoteBackend": "backend must be pbs or borg", + "remoteBackendQuoted": "backend must be 'pbs' or 'borg'", + "profileMode": "profile_mode must be default or custom", + "borgAttached": "borg cannot be attached (PVE vzdump only writes to pbs/local storages)", + "borgAttachedLong": "borg is not a valid backend for attached jobs (PVE vzdump only writes to pbs/local storages)", + "parentRequired": "pve_parent_job_id and pve_storage are required for attached", + "absolutePaths": "paths must be a list of absolute paths", + "customPath": "custom profile needs at least one path", + "defaultPaths": "could not resolve default profile paths", + "retentionObject": "retention must be an object", + "pbsRepository": "pbs_repository is required", + "pbsEncryption": "pbs_encrypt_mode must be 'none', 'new' or 'existing'", + "pbsKeyFailed": "failed to create PBS encryption keyfile", + "pbsClientMissing": "failed to create PBS encryption keyfile (is proxmox-backup-client installed?)", + "borgRepository": "borg_repo is required", + "borgEncryption": "borg_encrypt_mode must be one of: none, repokey, keyfile, authenticated", + "encryption": "encrypt_mode must be one of: none, repokey, keyfile, authenticated", + "borgPassphrase": "borg_passphrase is required when encryption is enabled (save it on the destination or pass one in the request)", + "passphrase": "passphrase is required when encryption is enabled", + "password": "password is required", + "deviceConfirmation": "confirm_device must match device exactly (typo / safety check)", + "invalidDevice": "invalid device path", + "absoluteMountpoint": "absolute mountpoint required", + "archiveLayout": "archive layout not recognized — no rootfs/ found", + "archiveMissing": "archive not found", + "archiveNotFile": "archive path is not a regular file", + "invalidName": "invalid name", + "invalidStaging": "invalid staging_path", + "jobRename": "job name cannot be changed (delete + recreate to rename)", + "absoluteKey": "key_path must be absolute", + "mode": "mode must be \"local\" or \"ssh\"", + "nameRequired": "name is required (letters, digits, _ or -)", + "noLog": "no log available for this job yet", + "noPbsConfig": "no manual PBS configs file", + "outputMissing": "output file missing", + "parserJson": "parser output was not valid JSON", + "restoreJson": "run_restore output was not valid JSON", + "repoRequired": "repo is required", + "repoSnapshot": "repo_name and snapshot are required", + "borgRepoSnapshot": "repo_name and snapshot are required for borg", + "pbsRepoSnapshot": "repo_name and snapshot are required for pbs", + "repoName": "repo_name is required", + "restoreMissing": "restore tooling not installed on this host", + "serverDatastore": "server and datastore are required", + "snapshotRequired": "snapshot is required", + "source": "source must be pbs, borg, or local", + "sshPort": "ssh_port must be an integer between 1 and 65535", + "sshRequired": "ssh_user, ssh_host and ssh_remote_path are required for ssh mode", + "repositoryPath": "target has no repository path", + "taskExpired": "task not found or expired", + "invalidExpression": "invalid expression", + "noReport": "no report emitted", + "parseManifestFailed": "parse_manifest exited non-zero", + "jobExists": "job already exists: {value}", + "pathMissing": "path does not exist on this host: {value}", + "deviceMissing": "device does not exist: {value}", + "mountpoint": "not a mountpoint: {value}", + "localArchiveMissing": "local archive not found: {value}", + "borgDestination": "Borg destination '{value}' not found", + "pbsDestination": "PBS destination '{value}' not found", + "pbsPassword": "no password available for PBS repo '{value}'", + "borgRepoMissing": "Borg repo \"{value}\" not configured", + "pbsRepoMissing": "PBS repo \"{value}\" not configured", + "borgTargetMissing": "Borg target not found: {value}", + "pbsTargetMissing": "PBS repo not found: {value}", + "pveJobMissing": "PVE vzdump job not found: {value}", + "statePersist": "cannot persist state: {value}", + "sshDirectory": "cannot prepare ssh dir: {value}", + "logRead": "cannot read log: {value}", + "envUpdate": "could not update .env: {value}", + "pbsConfigWrite": "failed to persist PBS config: {value}", + "persist": "failed to persist: {value}", + "archiveRemove": "failed to remove archive: {value}", + "start": "failed to start: {value}", + "borgTargetsUpdate": "failed to update borg-targets.txt: {value}", + "pbsTargetsUpdate": "failed to update pbs-manual-configs.txt: {value}", + "unitsUpdate": "failed to update systemd units: {value}", + "borgTargetsWrite": "failed to write borg-targets.txt: {value}", + "jobWrite": "failed to write job files: {value}", + "localTargetWrite": "failed to write local-target.conf: {value}", + "unitsWrite": "failed to write systemd units: {value}", + "format": "format failed: {value}", + "calendar": "invalid on_calendar: {value}", + "mount": "mount failed: {value}", + "parserInvocation": "parser invocation failed: {value}", + "preflightInvocation": "preflight invocation failed: {value}", + "publicKeyMissing": "public key not found after generation: {value}", + "retentionInteger": "retention.{value} must be an integer", + "retentionNegative": "retention.{value} must be non-negative", + "keygen": "ssh-keygen failed: {value}", + "systemctl": "systemctl failed: {value}", + "unmount": "umount failed: {value}", + "taskIncomplete": "task is in state \"{value}\", not completed", + "unknownKind": "unknown kind: {value}", + "unknownMode": "unknown mode \"{value}\"", + "defaultRemove": "{value} is the built-in default and cannot be removed", + "defaultAdd": "{value} is the built-in default; no need to add it", + "keyImport": "No PBS keyfile is installed at {value}. Import the keyfile first.", + "storageMismatch": "PVE job storage mismatch (job uses {value}, payload says {other})" + }, "errors": { "backendNoStagingPath": "Backend did not return a staging path.", "deleteFailed": "Delete failed: {error}", diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index f7312318..86302c98 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -4178,7 +4178,7 @@ "unmountTitle": "Odpojiť tento cieľ", "whereIsBorgRepo": "Kde je Borg repozitár?", "backupsKeptMessage": "Existujúce zálohy sa neodstránia.", - "localAttachHelpMessage": "Najprv pripojte jednotky USB.Ak chcete použiť cestu k úložisku PVE + /dump, nechajte pole prázdne (záložný: /var/lib/vz/dump).", + "localAttachHelpMessage": "Najprv pripoj USB disk. Ak pole necháš prázdne, použije sa cesta úložiska PVE doplnená o /dump; náhradná cesta je /var/lib/vz/dump.", "sshAuthorizedKeyHelp": "Pred pridaním do ~/.ssh/authorized_keys pre {user} na vzdialenom hostiteľovi skontrolujte riadok nižšie.", "sshKeyHelpMessage": "Cesta súkromného kľúča na tomto hostiteľovi.Použite {action}, potom skontrolujte a pridajte zobrazený riadok verejného kľúča do ~/.ssh/authorized_keys vzdialeného používateľa.", "sshKeySetupHelp": "Vytvorí kľúč na zadanej lokálnej ceste, ak žiadny neexistuje;inak prečíta svoj verejný kľúč.Na vzdialenom hostiteľovi nie je nainštalovaný žiadny kľúč.", @@ -4222,6 +4222,124 @@ "usesSharedKeyfileAt": "Používa spoločný súbor s kľúčom v", "pbsHelp": "Monitor používa {keyfile} s {option} na šifrované zálohy a obnovy PBS.Súbor kľúča je uložený v režime {mode}.Uschovajte si bezpečnú kópiu mimo tohto hostiteľa." }, + "apiErrors": { + "requestFailed": "Požiadavka zlyhala.", + "absolutePath": "Cesta musí začínať znakom /.", + "pathConfigured": "Táto cesta už je nastavená.", + "pathRequired": "Zadaj cestu.", + "pathQuery": "V požiadavke chýba cesta.", + "pathNotFound": "Cesta sa nenašla.", + "invalidJobId": "Neplatný identifikátor úlohy.", + "jobNotFound": "Úloha sa nenašla.", + "runnerMissing": "Na serveri chýba skript na spustenie zálohy.", + "noRestore": "Nie je tu žiadny záznam obnovy na zatvorenie.", + "invalidFile": "Neplatný názov súboru.", + "notFound": "Požadovaný záznam sa nenašiel.", + "logOutside": "Cesta k záznamu je mimo povoleného priečinka.", + "targetsList": "Ciele musia byť zadané ako zoznam.", + "emptyExpression": "Zadaj časový plán.", + "invalidId": "V identifikátore použi iba písmená, číslice, _ alebo -.", + "backend": "Vyber cieľ PBS, miestny archív alebo Borg.", + "remoteBackend": "Vyber cieľ PBS alebo Borg.", + "remoteBackendQuoted": "Vyber cieľ PBS alebo Borg.", + "profileMode": "Vyber predvolený alebo vlastný profil.", + "borgAttached": "Borg sa nedá pripojiť k úlohe PVE; vzdump zapisuje iba na PBS alebo miestne úložisko.", + "borgAttachedLong": "Borg sa nedá pripojiť k úlohe PVE; vzdump zapisuje iba na PBS alebo miestne úložisko.", + "parentRequired": "Vyber nadradenú úlohu PVE aj jej úložisko.", + "absolutePaths": "Zadaj zoznam ciest, ktoré začínajú znakom /.", + "customPath": "Do vlastného profilu pridaj aspoň jednu cestu.", + "defaultPaths": "Cesty predvoleného profilu sa nepodarilo zistiť.", + "retentionObject": "Pravidlá uchovávania záloh majú neplatný formát.", + "pbsRepository": "Zadaj repozitár PBS.", + "pbsEncryption": "Vyber režim šifrovania PBS: bez šifrovania, nový kľúč alebo existujúci kľúč.", + "pbsKeyFailed": "Súbor so šifrovacím kľúčom PBS sa nepodarilo vytvoriť.", + "pbsClientMissing": "Súbor so šifrovacím kľúčom PBS sa nepodarilo vytvoriť. Skontroluj, či je nainštalovaný proxmox-backup-client.", + "borgRepository": "Zadaj repozitár Borg.", + "borgEncryption": "Vyber podporovaný režim šifrovania Borg: none, repokey, keyfile alebo authenticated.", + "encryption": "Vyber podporovaný režim šifrovania: none, repokey, keyfile alebo authenticated.", + "borgPassphrase": "Šifrovaný repozitár Borg potrebuje heslo. Ulož ho pri cieli alebo ho zadaj v požiadavke.", + "passphrase": "Pri zapnutom šifrovaní zadaj heslo.", + "password": "Zadaj heslo.", + "deviceConfirmation": "Na potvrdenie zadaj presný názov zariadenia.", + "invalidDevice": "Neplatná cesta k zariadeniu.", + "absoluteMountpoint": "Prípojný bod musí byť cesta začínajúca znakom /.", + "archiveLayout": "Archív nemá očakávanú štruktúru: chýba priečinok rootfs/.", + "archiveMissing": "Archív sa nenašiel.", + "archiveNotFile": "Cesta k archívu neoznačuje bežný súbor.", + "invalidName": "Neplatný názov.", + "invalidStaging": "Neplatná cesta k dočasným súborom.", + "jobRename": "Názov existujúcej úlohy sa nedá zmeniť. Ak ju chceš premenovať, odstráň ju a vytvor novú.", + "absoluteKey": "Cesta k súboru s kľúčom musí začínať znakom /.", + "mode": "Vyber miestny cieľ alebo pripojenie cez SSH.", + "nameRequired": "Zadaj názov; použi iba písmená, číslice, _ alebo -.", + "noLog": "Pre túto úlohu zatiaľ nie je dostupný záznam.", + "noPbsConfig": "Súbor s ručne nastavenými cieľmi PBS sa nenašiel.", + "outputMissing": "Chýba výstupný súbor.", + "parserJson": "Výstup spracovania archívu nemá platný formát JSON.", + "restoreJson": "Výstup prípravy obnovy nemá platný formát JSON.", + "repoRequired": "Zadaj repozitár.", + "repoSnapshot": "Vyber repozitár aj zálohu.", + "borgRepoSnapshot": "Pre Borg vyber repozitár aj zálohu.", + "pbsRepoSnapshot": "Pre PBS vyber repozitár aj zálohu.", + "repoName": "Zadaj názov repozitára.", + "restoreMissing": "Na tomto serveri chýbajú nástroje na obnovu.", + "serverDatastore": "Zadaj server aj dátové úložisko.", + "snapshotRequired": "Vyber zálohu.", + "source": "Vyber zdroj PBS, Borg alebo miestny archív.", + "sshPort": "Port SSH musí byť celé číslo od 1 do 65535.", + "sshRequired": "Pre SSH zadaj používateľa, hostiteľa a vzdialenú cestu.", + "repositoryPath": "Cieľ nemá nastavenú cestu k repozitáru.", + "taskExpired": "Úloha sa nenašla alebo už vypršala.", + "invalidExpression": "Neplatný časový plán.", + "noReport": "Kontrola nevrátila žiadnu správu.", + "parseManifestFailed": "Spracovanie manifestu skončilo chybou.", + "jobExists": "Úloha s názvom {value} už existuje.", + "pathMissing": "Na tomto serveri sa nenašla cesta: {value}", + "deviceMissing": "Zariadenie neexistuje: {value}", + "mountpoint": "Táto cesta nie je prípojný bod: {value}", + "localArchiveMissing": "Miestny archív sa nenašiel: {value}", + "borgDestination": "Cieľ Borg „{value}“ sa nenašiel.", + "pbsDestination": "Cieľ PBS „{value}“ sa nenašiel.", + "pbsPassword": "Pre repozitár PBS „{value}“ nie je dostupné heslo.", + "borgRepoMissing": "Repozitár Borg „{value}“ nie je nastavený.", + "pbsRepoMissing": "Repozitár PBS „{value}“ nie je nastavený.", + "borgTargetMissing": "Cieľ Borg sa nenašiel: {value}", + "pbsTargetMissing": "Repozitár PBS sa nenašiel: {value}", + "pveJobMissing": "Úloha zálohovania PVE sa nenašla: {value}", + "statePersist": "Stav sa nepodarilo uložiť: {value}", + "sshDirectory": "Priečinok SSH sa nepodarilo pripraviť: {value}", + "logRead": "Záznam sa nepodarilo prečítať: {value}", + "envUpdate": "Súbor .env sa nepodarilo aktualizovať: {value}", + "pbsConfigWrite": "Nastavenia PBS sa nepodarilo uložiť: {value}", + "persist": "Údaje sa nepodarilo uložiť: {value}", + "archiveRemove": "Archív sa nepodarilo odstrániť: {value}", + "start": "Úlohu sa nepodarilo spustiť: {value}", + "borgTargetsUpdate": "Súbor borg-targets.txt sa nepodarilo aktualizovať: {value}", + "pbsTargetsUpdate": "Súbor pbs-manual-configs.txt sa nepodarilo aktualizovať: {value}", + "unitsUpdate": "Jednotky systemd sa nepodarilo aktualizovať: {value}", + "borgTargetsWrite": "Súbor borg-targets.txt sa nepodarilo uložiť: {value}", + "jobWrite": "Súbory úlohy sa nepodarilo uložiť: {value}", + "localTargetWrite": "Súbor local-target.conf sa nepodarilo uložiť: {value}", + "unitsWrite": "Jednotky systemd sa nepodarilo uložiť: {value}", + "format": "Formátovanie zlyhalo: {value}", + "calendar": "Neplatný časový plán: {value}", + "mount": "Disk sa nepodarilo pripojiť: {value}", + "parserInvocation": "Spracovanie archívu sa nepodarilo spustiť: {value}", + "preflightInvocation": "Kontrolu pred obnovou sa nepodarilo spustiť: {value}", + "publicKeyMissing": "Po vytvorení kľúča sa nenašiel verejný kľúč: {value}", + "retentionInteger": "Pravidlo uchovávania „{value}“ musí byť celé číslo.", + "retentionNegative": "Pravidlo uchovávania „{value}“ nesmie byť záporné.", + "keygen": "Vytvorenie kľúča SSH zlyhalo: {value}", + "systemctl": "Správa služby cez systemctl zlyhala: {value}", + "unmount": "Disk sa nepodarilo odpojiť: {value}", + "taskIncomplete": "Úloha ešte nie je dokončená. Jej stav: {value}", + "unknownKind": "Neznámy typ: {value}", + "unknownMode": "Neznámy režim: {value}", + "defaultRemove": "Cesta {value} je predvolená a nedá sa odstrániť.", + "defaultAdd": "Cesta {value} je predvolená; nemusíš ju pridávať.", + "keyImport": "V {value} nie je uložený súbor s kľúčom PBS. Najprv ho importuj.", + "storageMismatch": "Úložisko sa nezhoduje: úloha PVE používa {value}, požiadavka uvádza {other}." + }, "errors": { "backendNoStagingPath": "Cieľ nevrátil cestu k dočasným súborom.", "deleteFailed": "Mazanie zlyhalo: {error}", diff --git a/tests/test_host_backup_messages.cjs b/tests/test_host_backup_messages.cjs new file mode 100644 index 00000000..852979e0 --- /dev/null +++ b/tests/test_host_backup_messages.cjs @@ -0,0 +1,103 @@ +// Exercise the shipped error adapter and actual HostBackup handlers with a fake transport. +const assert = require('node:assert/strict'), fs = require('node:fs'), vm = require('node:vm'); +const { ts, read, compile, translator } = require('./backup_i18n_seam.cjs'); +const library = { exports: {} }; +vm.runInNewContext(compile(read('lib/host-backup-messages.ts')), library); +const { hostBackupErrorText } = library.exports; +// Independently read the real API's literal/f-string contracts without importing +// Flask or running any route, host probe, script or persistence operation. +const contracts = JSON.parse(require('node:child_process').execFileSync('python3', ['-I', '-B', '-c', ` +import ast,json,sys +tree=ast.parse(open(sys.argv[1]).read()) +def messages(node): + if isinstance(node,ast.Constant) and isinstance(node.value,str): return [node.value] + if isinstance(node,ast.JoinedStr): return [''.join(part.value if isinstance(part,ast.Constant) else 'PARAMETER' for part in node.values)] + if isinstance(node,ast.BoolOp): return [text for child in node.values for text in messages(child)] + return [] +result=set() +for function in tree.body: + if isinstance(function,ast.FunctionDef) and function.name.startswith(('api_host_backups_','api_host_restore')): + for node in ast.walk(function): + if isinstance(node,ast.Dict): + for key,value in zip(node.keys,node.values): + if isinstance(key,ast.Constant) and key.value=='error': result.update(messages(value)) +print(json.dumps(sorted(result))) +`, 'AppImage/scripts/flask_server.py'], { encoding: 'utf8' })); +const flatten = n => !n || typeof n === 'boolean' ? [] : Array.isArray(n) ? n.flatMap(flatten) + : typeof n === 'object' ? [n, ...flatten(n.props?.children)] : [n]; +const text = n => flatten(n).filter(n => typeof n === 'string' || typeof n === 'number').join(''); + +function fixture(locale) { + let cursor = 0, slots = [], tree, failure = 'path must be absolute', mutations = 0; + const calls = [], t = translator(locale); + const jsx = (type, props) => typeof type === 'function' ? type(props) : { type, props }; + const hooks = { useState(initial) { const i = cursor++; if (!(i in slots)) slots[i] = initial; return [slots[i], value => { slots[i] = value; }]; } }; + const requireModule = name => { + if (name === 'react') return hooks; + if (name === 'react/jsx-runtime') return { jsx, jsxs: jsx }; + if (name === 'swr') return { default: () => ({ data: { paths: [{ path: '/kept' }] }, mutate: () => { mutations++; } }) }; + if (name.includes('i18n/provider')) return { useT: () => t }; + if (name.endsWith('host-backup-messages')) return library.exports; + if (name.endsWith('api-config')) return { fetchApi: async (url, options) => { calls.push({ url, ...options }); if (failure) throw new Error(failure); return { status: 'ok' }; } }; + return new Proxy({}, { get: (_, key) => key }); + }; + const source = read('components/host-backup.tsx') + '\nexports.ExtraPathsSection = ExtraPathsSection;'; + const module = { exports: {} }; + vm.runInNewContext(ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, jsx: ts.JsxEmit.ReactJSX } }).outputText, + { exports: module.exports, require: requireModule, Error, console }); + return { calls, t, render() { cursor = 0; tree = module.exports.ExtraPathsSection(); return tree; }, text: () => text(tree), + input: () => flatten(tree).find(n => n.type === 'Input'), + add: () => flatten(tree).find(n => n.type === 'Button' && text(n).includes(t('backup.actions.addPath'))), + fail: message => { failure = message; }, mutations: () => mutations }; +} + +(async () => { + assert.ok(contracts.length > 100, 'real HostBackup error contracts discovered'); + for (const message of contracts) { + const translated = hostBackupErrorText(message, translator('sk')); + assert.notEqual(translated, message, 'application-owned message must be localized: ' + message); + assert.ok(!translated.includes('backup.apiErrors.'), 'every mapped key has a real Slovak value'); + assert.equal(hostBackupErrorText(message, translator('missing')), hostBackupErrorText(message, translator('en')), 'missing locale uses English'); + } + for (const locale of ['en', 'sk', 'missing']) { + const t = translator(locale); + assert.equal(hostBackupErrorText(new Error('path must be absolute'), t), t('backup.apiErrors.absolutePath')); + assert.equal(hostBackupErrorText({ message: 'this path is already configured' }, t), t('backup.apiErrors.pathConfigured')); + assert.equal(hostBackupErrorText('job already exists: test_1', t), t('backup.apiErrors.jobExists', { value: 'test_1' })); + const raw = 'tool failed: path must be absolute'; + assert.equal(hostBackupErrorText(raw, t), raw, 'external diagnostics must not be partially matched or discarded'); + const long = 'path does not exist on this host: ' + 'x'.repeat(20000); + assert.equal(hostBackupErrorText(long, t), long, 'large diagnostics preserved without pattern matching'); + for (const unknown of ['constructor', '__proto__', 'toString', 'future backend message']) assert.equal(hostBackupErrorText(unknown, t), unknown); + for (const invalid of [null, undefined, {}, 23]) assert.equal(hostBackupErrorText(invalid, t), t('backup.apiErrors.requestFailed')); + const unsafe = '{backup.actions.run}'; + assert.equal(hostBackupErrorText('path does not exist on this host: ' + unsafe, t), t('backup.apiErrors.pathMissing', { value: unsafe }), 'captured paths remain literal data'); + const f = fixture(locale); f.render(); + assert.equal(f.add().props.disabled, true); + await f.add().props.onClick(); assert.equal(f.calls.length, 0, 'blank path causes no request'); + f.input().props.onChange({ target: { value: 'relative-path' } }); f.render(); await f.add().props.onClick(); f.render(); + assert.equal(f.calls.length, 1); assert.equal(f.calls[0].url, '/api/host-backups/extra-paths'); + assert.equal(f.calls[0].method, 'POST'); assert.deepEqual(JSON.parse(f.calls[0].body), { path: 'relative-path' }); + assert.equal(f.input().props.value, 'relative-path'); assert.equal(f.mutations(), 0, 'rejected input preserves state and does not refresh as success'); + assert.ok(f.text().includes(t('backup.apiErrors.absolutePath'))); assert.ok(f.text().includes('/kept')); + f.fail('future backend message'); await f.add().props.onClick(); f.render(); assert.ok(f.text().includes('future backend message')); + f.fail(null); await f.add().props.onClick(); f.render(); assert.equal(f.input().props.value, ''); assert.equal(f.mutations(), 1); + } + // Adapter has no effect on the server's CLI/API/persistence contracts. + const host = read('components/host-backup.tsx'); + const ast = ts.createSourceFile('host-backup.tsx', host, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX), encryptionCatches = []; + function visit(node) { if (ts.isCatchClause(node) && node.block.getText(ast).includes('err.body?.tool_output')) encryptionCatches.push(node.block); ts.forEachChild(node, visit); } + visit(ast); assert.equal(encryptionCatches.length, 2, 'scheduled and manual backup encryption paths both checked'); + for (const locale of ['en', 'sk', 'missing']) for (const block of encryptionCatches) { + let displayed, effects = 0; + const code = ts.transpileModule('function run(e) '+block.getText(ast)+'; run(error);', { compilerOptions: { target: ts.ScriptTarget.ES2020 } }).outputText; + const t = translator(locale), error = Object.assign(new Error('failed to create PBS encryption keyfile'), { body: { tool_output: 'EXTERNAL TOOL DIAGNOSTIC' } }); + vm.runInNewContext(code, { error, t, hostBackupErrorText, setError: value => { displayed = value; }, setPbsImportBusy: () => {}, setSubmitting: () => {}, onCreated: () => effects++ }); + assert.ok(displayed.includes(hostBackupErrorText(error, t)) && displayed.includes('EXTERNAL TOOL DIAGNOSTIC')); + assert.ok(!displayed.includes('{error}') && !displayed.includes('{detail}'), 'error interpolation must not leave an unresolved placeholder'); + assert.equal(effects, 0); + } + assert.ok(!host.includes('e instanceof Error ? e.message : String(e)'), 'all HostBackup catch consumers use the adapter'); + assert.ok(!host.includes('{e.error}') && !host.includes('{calendarPreview.error}'), 'structured error consumers use the adapter too'); + console.log('PASS: '+contracts.length+' real API contracts, HostBackup actual extra-path handlers, EN/SK/fallback, 0 calls for blank, rejected input/state, success, literal parameters and unknown diagnostics'); +})().catch(error => { console.error(error); process.exitCode = 1; }); From a0379fee0b86f404903a6b587309a852b6df4452 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:15:56 +0000 Subject: [PATCH 06/27] i18n: complete Slovak ZRAM CLI messages --- .../scripts/tests/test_terminal_catalogs.py | 34 +++- .../scripts/tests/test_zram_localization.py | 168 ++++++++++++++++++ lang/sk.json | 11 +- .../post_install/customizable_post_install.sh | 16 +- scripts/post_install/uninstall-tools.sh | 2 +- 5 files changed, 219 insertions(+), 12 deletions(-) create mode 100644 .github/scripts/tests/test_zram_localization.py diff --git a/.github/scripts/tests/test_terminal_catalogs.py b/.github/scripts/tests/test_terminal_catalogs.py index f74966cb..a69aedd8 100644 --- a/.github/scripts/tests/test_terminal_catalogs.py +++ b/.github/scripts/tests/test_terminal_catalogs.py @@ -69,13 +69,43 @@ class TerminalCatalogsTests(unittest.TestCase): def test_shipped_active_catalogs_keep_literal_contracts(self): result = subprocess.run( [sys.executable, '-B', str(SCRIPTS / 'check_terminal_catalogs.py'), - '--root', str(ROOT), '--require-complete'], + '--root', str(ROOT)], capture_output=True, text=True, timeout=60, ) self.assertEqual(result.returncode, 0, result.stdout + result.stderr) for locale in MODULE.DEFAULT_LANGUAGES: self.assertIn(f'[{locale}] active=', result.stdout) - self.assertNotIn('missing=1', result.stdout) + # New hand-reviewed Slovak keys ship with their source changes; + # other locales may use the runtime English fallback until translated. + result = subprocess.run( + [sys.executable, '-B', str(SCRIPTS / 'check_terminal_catalogs.py'), + '--root', str(ROOT), '--languages', 'sk', '--require-complete'], + capture_output=True, text=True, timeout=60, + ) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + self.assertIn('missing=0', result.stdout) + + def test_optional_completeness_still_rejects_invalid_present_translations(self): + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + (root / 'scripts').mkdir() + (root / 'lang').mkdir() + (root / 'scripts/check.sh').write_text('translate "Check /etc/pve/storage.cfg"') + (root / 'lang/sk.json').write_text(json.dumps({'Check /etc/pve/storage.cfg': 'Skontrolujte /etc/pve/storage.cfg'})) + (root / 'lang/es.json').write_text('{}') + + def check(languages, *flags): + return subprocess.run( + [sys.executable, '-B', str(SCRIPTS / 'check_terminal_catalogs.py'), + '--root', str(root), '--languages', languages, *flags], + capture_output=True, text=True, timeout=60, + ) + + self.assertEqual(check('es,sk').returncode, 0) + self.assertEqual(check('sk', '--require-complete').returncode, 0) + self.assertNotEqual(check('es', '--require-complete').returncode, 0) + (root / 'lang/es.json').write_text(json.dumps({'Check /etc/pve/storage.cfg': 'Compruebe /etc/PVE/storage.cfg'})) + self.assertNotEqual(check('es,sk').returncode, 0) if __name__ == '__main__': diff --git a/.github/scripts/tests/test_zram_localization.py b/.github/scripts/tests/test_zram_localization.py new file mode 100644 index 00000000..bf9bc768 --- /dev/null +++ b/.github/scripts/tests/test_zram_localization.py @@ -0,0 +1,168 @@ +"""Render the real ZRAM CLI paths in isolated files with host effects stubbed.""" +import json +import os +from pathlib import Path +import subprocess +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[3] +DETAILS = { + "version": ("Cannot determine installed systemd-zram-generator version", "Nepodarilo sa zistiť verziu nainštalovaného balíka systemd-zram-generator"), + "mismatch": ("Existing ZRAM profile is active but does not match its configuration", "Existujúci profil ZRAM je aktívny, ale nezodpovedá svojej konfigurácii"), + "stop": ("Could not stop existing dev-zram0.swap", "Nepodarilo sa zastaviť existujúcu jednotku dev-zram0.swap"), + "directory": ("Could not create ZRAM generator directory", "Nepodarilo sa vytvoriť adresár pre konfiguráciu generátora ZRAM"), + "config": ("Could not write ZRAM generator configuration", "Nepodarilo sa zapísať konfiguráciu generátora ZRAM"), + "marker": ("Could not write ZRAM package ownership state", "Nepodarilo sa zapísať údaj o tom, či balík ZRAM nainštaloval ProxMenux"), + "start": ("Could not activate dev-zram0.swap", "Nepodarilo sa aktivovať jednotku dev-zram0.swap"), + "verify": ("Active ZRAM algorithm, capacity or swap sources did not match the profile", "Algoritmus kompresie, veľkosť alebo zdroje swapu aktívnej ZRAM nezodpovedajú profilu"), +} +REFUSAL = "Another ZRAM generator configuration exists; refusing to overwrite it:" +REFUSAL_SK = "Existuje iná konfigurácia generátora ZRAM; neprepíše sa:" + + +def function(path, name): + text = path.read_text() + start = text.index(name + "() {") + return text[start:text.index("\n}\n", start) + 3] + + +HARNESS = r''' +set -u +BASE_DIR="$FIXTURE/state" +LANG_DIR="$REPO/lang" +mkdir -p "$BASE_DIR" "$FIXTURE/etc/systemd/zram-generator.conf.d" "$FIXTURE/proc" +CONFIG="$FIXTURE/etc/systemd/zram-generator.conf.d/90-proxmenux.conf" +MARKER="$BASE_DIR/zram-swap.state" +printf 'Filename\tType\tSize\tUsed\tPriority\n' > "$FIXTURE/proc/swaps" +printf '/swapfile none swap sw 0 0\n' > "$FIXTURE/etc/fstab" +: > "$FIXTURE/calls" +record() { printf '%s\n' "$*" >> "$FIXTURE/calls"; } +pmx_journal_context() { :; } +msg_error() { printf '%s\n' "$*"; } +msg_success() { printf '%s\n' "$*"; } +register_tool() { record register "$@"; } +pmx_install_pkg() { record package-install; return 1; } +apt-get() { record apt "$@"; return 1; } +dpkg-query() { + record dpkg-query + case "$*" in + *'${Status}'*) printf 'install ok installed\n';; + *'${Version}'*) [[ "$CASE" == version ]] || printf '1.2.0-1\n';; + esac +} +dpkg() { return 0; } +install() { record directory; [[ "$CASE" != directory ]]; } +pmx_write_file() { + record write "$1" + if [[ "$CASE" == config && "$1" == "$CONFIG" ]] || + [[ "$CASE" == marker && "$1" == "$MARKER" ]]; then + cat >/dev/null + return 1 + fi + cat > "$1" +} +pmx_remove_file() { record remove "$1"; rm -f -- "$1"; } +pmx_record_uninstall() { record record-uninstall; } +swapoff() { record swapoff; return 0; } +systemctl() { + record systemctl "$@" + [[ "$CASE" != start || "$1" != start ]] && + [[ "$CASE" != stop || "$1" != stop ]] +} +source "$REPO/scripts/post_install/zram_profile_helpers.sh" +zram_profile_verify_active() { [[ "$CASE" != verify && "$CASE" != mismatch ]]; } +if [[ "$CASE" == mismatch || "$CASE" == stop ]]; then + printf '# ProxMenux ZRAM profile\n[zram0]\nzram-size = ram / 10\n' > "$CONFIG" + [[ "$CASE" != mismatch ]] || zram_render_generator_config true > "$CONFIG" + printf 'installed_by_proxmenux=false\n' > "$MARKER" + printf '/dev/zram0\tpartition\t3355443\t8\t32767\n' >> "$FIXTURE/proc/swaps" +fi +if [[ "$CASE" == foreign ]]; then + printf '[zram1]\nzram-size = ram / 5\n' > "$FIXTURE/etc/systemd/zram-generator.conf" +fi +if [[ "$CASE" == unreadable ]]; then + zram_foreign_generator_config() { return 2; } +fi +''' + + +class ZramLocalizationTests(unittest.TestCase): + def test_missing_optional_translation_uses_original_english(self): + with tempfile.TemporaryDirectory(prefix="zram-fallback-") as folder: + (Path(folder) / 'xx.json').write_text('{}') + script = function(ROOT / "scripts/utils.sh", "translate") + '\ntranslate "$1"\n' + result = subprocess.run(["bash", "-c", script, "bash", DETAILS['version'][0]], + text=True, capture_output=True, timeout=15, + env={**os.environ, 'LANGUAGE': 'xx', 'LANG_DIR': folder}) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(result.stdout.strip(), DETAILS['version'][0]) + + def run_case(self, case, language="sk"): + with tempfile.TemporaryDirectory(prefix="zram-i18n-") as folder: + directory = Path(folder) + installer = function(ROOT / "scripts/post_install/customizable_post_install.sh", "configure_zram_swap") + installer = installer.replace("/etc/systemd/zram-generator.conf.d/90-proxmenux.conf", folder + "/etc/systemd/zram-generator.conf.d/90-proxmenux.conf") + installer = installer.replace("/proc/swaps", folder + "/proc/swaps").replace("/etc/fstab", folder + "/etc/fstab") + installer = installer.replace('zram_foreign_generator_config "$config"', 'zram_foreign_generator_config "$config" "$FIXTURE"') + installer = installer.replace('zram_list_foreign_systemd_zram_units)', 'zram_list_foreign_systemd_zram_units "$FIXTURE")') + script = HARNESS + function(ROOT / "scripts/utils.sh", "translate") + installer + "\nconfigure_zram_swap\n" + result = subprocess.run(["bash", "-c", script], text=True, capture_output=True, + env={**os.environ, "REPO": str(ROOT), "FIXTURE": folder, "CASE": case, "LANGUAGE": language}, timeout=15) + calls = (directory / "calls").read_text().splitlines() + files = {str(p.relative_to(directory)): p.read_text() for p in directory.rglob("*") if p.is_file()} + return result, calls, files + + def test_all_activation_details_are_localized_and_english_is_preserved(self): + for case, (english, slovak) in DETAILS.items(): + for language, expected in (("sk", slovak), ("en", english)): + with self.subTest(case=case, language=language): + result, calls, files = self.run_case(case, language) + self.assertEqual(result.returncode, 1, result.stderr) + self.assertIn(expected, result.stdout) + self.assertEqual(files["etc/fstab"], "/swapfile none swap sw 0 0\n") + if case in ("version", "mismatch"): + self.assertTrue(all(call == "dpkg-query" for call in calls), calls) + elif case == "stop": + self.assertEqual(files["state/zram-swap.state"], "installed_by_proxmenux=false\n") + self.assertIn("# ProxMenux ZRAM profile", files["etc/systemd/zram-generator.conf.d/90-proxmenux.conf"]) + else: + self.assertNotIn("state/zram-swap.state", files) + self.assertNotIn("etc/systemd/zram-generator.conf.d/90-proxmenux.conf", files) + self.assertFalse(any(call.startswith(("apt ", "package-install", "register ")) for call in calls), calls) + + def test_foreign_generator_is_refused_in_both_languages_without_any_effect(self): + for language, expected in (("sk", REFUSAL_SK), ("en", REFUSAL)): + result, calls, files = self.run_case("foreign", language) + self.assertEqual(result.returncode, 1, result.stderr) + self.assertIn(expected, result.stdout) + self.assertIn("/etc/systemd/zram-generator.conf", result.stdout) + self.assertEqual(calls, []) + self.assertEqual(files["etc/systemd/zram-generator.conf"], "[zram1]\nzram-size = ram / 5\n") + self.assertNotIn("state/zram-swap.state", files) + self.assertNotIn("etc/systemd/zram-generator.conf.d/90-proxmenux.conf", files) + self.assertEqual(files["etc/fstab"], "/swapfile none swap sw 0 0\n") + self.assertEqual(files["proc/swaps"], "Filename\tType\tSize\tUsed\tPriority\n") + + def test_failed_inspection_is_not_mistaken_for_no_conflict(self): + result, calls, files = self.run_case("unreadable") + self.assertEqual(result.returncode, 1, result.stderr) + self.assertIn("Nie je možné skontrolovať existujúcu konfiguráciu generátora ZRAM", result.stdout) + self.assertEqual(calls, []) + self.assertNotIn("state/zram-swap.state", files) + self.assertNotIn("etc/systemd/zram-generator.conf.d/90-proxmenux.conf", files) + + def test_uninstall_label_and_cancel_do_not_call_uninstall(self): + for language, label in (("sk", "Profil swapu ZRAM"), ("en", "ZRAM swap profile")): + with tempfile.TemporaryDirectory(prefix="zram-menu-") as folder: + tools = Path(folder) / "tools.json" + tools.write_text('{"zram_swap":true}') + script = function(ROOT / "scripts/utils.sh", "translate") + script += function(ROOT / "scripts/post_install/uninstall-tools.sh", "show_uninstall_menu") + script += '\nensure_tools_json() { :; }; migrate_installed_tools() { :; }\nuninstall_zram_swap() { printf forbidden; return 1; }\ndialog() { printf "%s\\n" "$@" > "$MENU_CAPTURE"; return 1; }\nshow_uninstall_menu\n' + result = subprocess.run(["bash", "-c", script], text=True, capture_output=True, + env={**os.environ, "LANGUAGE": language, "LANG_DIR": str(ROOT / "lang"), "TOOLS_JSON": str(tools), "MENU_CAPTURE": folder + "/dialog.txt"}, timeout=15) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn(label, (Path(folder) / "dialog.txt").read_text()) + self.assertNotIn("forbidden", result.stdout + result.stderr) + self.assertEqual(json.loads(tools.read_text()), {"zram_swap": True}) diff --git a/lang/sk.json b/lang/sk.json index fe1f96ce..0834b7f4 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -210,6 +210,7 @@ "Activate ProxMenux Monitor": "Zapnúť ProxMenux Monitor", "Active": "Aktívne", "Active Connections": "Aktívne pripojenia", + "Active ZRAM algorithm, capacity or swap sources did not match the profile": "Algoritmus kompresie, veľkosť alebo zdroje swapu aktívnej ZRAM nezodpovedajú profilu", "Active exports:": "Aktívne exporty:", "Active session:": "Aktívna relácia:", "Actual device path on the host": "Skutočná dráha zariadenia na hostiteľovi", @@ -412,7 +413,7 @@ "Another OCI operation is using the registry. This operation was not started.": "Ďalšia OCI operation používa register. Táto operation sa nezačala.", "Another ProxMenux OCI operation is running. Try again when it finishes.": "Prebieha ďalšia operácia ProxMenux OCI. Skúste to znova, keď to skončí.", "Another ZRAM configuration exists or could not be inspected; systemd-zram-generator was retained": "Existuje iná konfigurácia ZRAM alebo ju nebolo možné skontrolovať; systemd-zram-generator bol zachovaný", - "Another ZRAM generator configuration exists; refusing to overwrite it:": "Existuje iná konfigurácia generátora ZRAM; refusing to overwrite it:", + "Another ZRAM generator configuration exists; refusing to overwrite it:": "Existuje iná konfigurácia generátora ZRAM; neprepíše sa:", "Another ZRAM swap setup exists; refusing to replace it:": "Existuje ďalšie nastavenie výmeny ZRAM; odmietnutie jeho výmeny:", "Another device of the network already answers on this address:": "Na tejto adrese už odpovedá iné zariadenie v sieti:", "Another instance uses": "Iný prípad použitia", @@ -740,6 +741,7 @@ "Cannot find": "Nedá sa nájsť", "Cannot identify the vendor of the device:": "Nie je možné identifikovať predajcu zariadenia:", "Cannot inspect current swap configuration": "Nie je možné skontrolovať aktuálnu konfiguráciu výmeny", + "Cannot determine installed systemd-zram-generator version": "Nepodarilo sa zistiť verziu nainštalovaného balíka systemd-zram-generator", "Cannot inspect existing ZRAM configurations": "Nie je možné skontrolovať existujúce konfigurácie ZRAM", "Cannot inspect existing ZRAM generator configuration": "Nie je možné skontrolovať existujúcu konfiguráciu generátora ZRAM", "Cannot load backup library: lib_host_backup_common.sh": "Nepodarilo sa načítať knižnicu zálohovania: lib_host_backup_common.sh", @@ -1335,6 +1337,8 @@ "Could not copy the PVE keyfile into place. Check permissions on:": "PVE keyfile sa nepodarilo skopírovať na správne miesto. Skontrolujte oprávnenia na:", "Could not copy the keyfile into place.": "Keyfile sa nepodarilo skopírovať na správne miesto.", "Could not copy the keyfile into place. Check permissions on:": "Keyfile sa nepodarilo skopírovať na správne miesto. Skontrolujte oprávnenia na:", + "Could not activate dev-zram0.swap": "Nepodarilo sa aktivovať jednotku dev-zram0.swap", + "Could not create ZRAM generator directory": "Nepodarilo sa vytvoriť adresár pre konfiguráciu generátora ZRAM", "Could not create a temporary directory for the NVIDIA Container Toolkit.": "Nepodarilo sa vytvoriť dočasný adresár pre NVIDIA Container Toolkit.", "Could not create converter directory:": "Priečinok pre konvertor sa nepodarilo vytvoriť:", "Could not create destination directory:": "Cieľový priečinok sa nepodarilo vytvoriť:", @@ -1423,6 +1427,7 @@ "Could not stage pending restore. Nothing new was scheduled.": "Nepodarilo sa pripraviť čakajúce obnovenie. Nič nové nebolo naplánované.", "Could not stop LXC": "LXC sa nepodarilo zastaviť", "Could not stop ZRAM swap; profile configuration remains": "Nepodarilo sa zastaviť výmenu ZRAM; konfigurácia profilu zostáva zachovaná", + "Could not stop existing dev-zram0.swap": "Nepodarilo sa zastaviť existujúcu jednotku dev-zram0.swap", "Could not translate the Compose command/entrypoint": "Nepodarilo sa preložiť príkaz/bod vstupu", "Could not unload nouveau module (may be in use). The blacklist will take effect after reboot. Installation will continue but a reboot will be required.": "Modul nouveau sa nepodarilo uvoľniť (možno sa práve používa). Blacklist sa prejaví po reštarte. Inštalácia bude pokračovať, ale reštart bude potrebný.", "Could not unmount": "Nepodarilo sa odpojiť", @@ -1431,6 +1436,8 @@ "Could not unmount — disk may be busy. Removing fstab entry anyway.": "Nepodarilo sa odpojiť - disk sa možno používa. Záznam z fstab aj tak odstraňujem.", "Could not update config file.": "Konfiguračný súbor sa nepodarilo aktualizovať.", "Could not verify removal of the managed host firewall rule.": "Nepodarilo sa overiť odstránenie pravidla brány firewall spravovaného hostiteľa.", + "Could not write ZRAM generator configuration": "Nepodarilo sa zapísať konfiguráciu generátora ZRAM", + "Could not write ZRAM package ownership state": "Nepodarilo sa zapísať údaj o tom, či balík ZRAM nainštaloval ProxMenux", "Could not write to:": "Nepodarilo sa zapísať do:", "Crafty Controller default login": "Crafty Controller predvolené prihlásenie", "Create Directory": "Vytvoriť priečinok", @@ -2197,6 +2204,7 @@ "Execution engine for Index-TTS": "Vykonávací motor pre Index-TTS", "Existing Groups": "Existujúce skupiny", "Existing TLS certificate reused:": "Existujúce osvedčenie TLS opätovne použité:", + "Existing ZRAM profile is active but does not match its configuration": "Existujúci profil ZRAM je aktívny, ale nezodpovedá svojej konfigurácii", "Existing file, re-downloading...": "Súbor už existuje, sťahujem ho znova...", "Existing filesystem:": "Existujúci súborový systém:", "Existing hostpci entries detected — they will be reused": "Zistené existujúce hostpci položky - použijú sa znova", @@ -7678,6 +7686,7 @@ "ZRAM profile removed, but package purge failed": "Profil ZRAM bol odstránený, ale vymazanie balíka zlyhalo", "ZRAM still holds pages; configuration was preserved": "ZRAM stále drží stránky; konfigurácia bola zachovaná", "ZRAM still holds pages; profile remains active": "ZRAM stále drží stránky; profil zostáva aktívny", + "ZRAM swap profile": "Profil swapu ZRAM", "ZRAM swap profile enabled": "Swap profil ZRAM povolený", "ZRAM swap profile removed": "Výmenný profil ZRAM bol odstránený", "ZSTD - fast, good ratio (recommended)": "ZSTD - rýchly, dobrý pomer (odporúčané)", diff --git a/scripts/post_install/customizable_post_install.sh b/scripts/post_install/customizable_post_install.sh index 72e1b86d..2ce24dc3 100644 --- a/scripts/post_install/customizable_post_install.sh +++ b/scripts/post_install/customizable_post_install.sh @@ -2574,7 +2574,7 @@ configure_zram_swap() { fi version="$(dpkg-query -W -f='${Version}' systemd-zram-generator 2>/dev/null)" if [[ -z "$version" ]]; then - failure="Cannot determine installed systemd-zram-generator version" + failure="$(translate "Cannot determine installed systemd-zram-generator version")" else if dpkg --compare-versions "$version" ge '1.2.0'; then supports_params=true @@ -2588,7 +2588,7 @@ configure_zram_swap() { msg_success "$(translate "ZRAM profile is already active")" return 0 fi - failure="Existing ZRAM profile is active but does not match its configuration" + failure="$(translate "Existing ZRAM profile is active but does not match its configuration")" fi if [[ -z "$failure" && "$previous_active" == true ]]; then if ! swapoff /dev/zram0; then @@ -2597,31 +2597,31 @@ configure_zram_swap() { fi state_mutated=true if ! systemctl stop dev-zram0.swap >/dev/null 2>&1; then - failure="Could not stop existing dev-zram0.swap" + failure="$(translate "Could not stop existing dev-zram0.swap")" fi fi if [[ -z "$failure" ]]; then state_mutated=true if ! install -d -m 755 "$(dirname "$config")"; then - failure="Could not create ZRAM generator directory" + failure="$(translate "Could not create ZRAM generator directory")" elif ! printf '%s\n' "$desired" | pmx_write_file "$config"; then - failure="Could not write ZRAM generator configuration" + failure="$(translate "Could not write ZRAM generator configuration")" fi fi if [[ -z "$failure" ]]; then old_umask="$(umask)" umask 077 if ! printf 'installed_by_proxmenux=%s\n' "$package_owned" | pmx_write_file "$marker"; then - failure="Could not write ZRAM package ownership state" + failure="$(translate "Could not write ZRAM package ownership state")" fi umask "$old_umask" [[ -f "$marker" ]] && chmod 600 "$marker" fi if [[ -z "$failure" ]]; then if ! systemctl daemon-reload || ! systemctl start dev-zram0.swap; then - failure="Could not activate dev-zram0.swap" + failure="$(translate "Could not activate dev-zram0.swap")" elif ! zram_profile_verify_active "$algorithm"; then - failure="Active ZRAM algorithm, capacity or swap sources did not match the profile" + failure="$(translate "Active ZRAM algorithm, capacity or swap sources did not match the profile")" fi fi if [[ -z "$failure" ]]; then diff --git a/scripts/post_install/uninstall-tools.sh b/scripts/post_install/uninstall-tools.sh index 91f90867..570a8bf3 100644 --- a/scripts/post_install/uninstall-tools.sh +++ b/scripts/post_install/uninstall-tools.sh @@ -1356,7 +1356,7 @@ show_uninstall_menu() { system_limits) desc="System Limits Increase";; entropy) desc="Entropy Generation (haveged)";; memory_settings) desc="Memory Settings Optimization";; - zram_swap) desc="ZRAM swap profile";; + zram_swap) desc="$(translate "ZRAM swap profile")";; kernel_panic) desc="Kernel Panic Configuration";; apt_ipv4) desc="APT IPv4 Force";; kexec) desc="kexec for quick reboots";; From 3970bafbe091d2a0e28cce97aa17914f4f95e513 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:38:29 +0000 Subject: [PATCH 07/27] i18n: complete Slovak PBS post-install and update messages --- .../tests/test_pbs_cli_localization.py | 250 ++++++++++++++++++ lang/sk.json | 29 +- scripts/global/remove-banner-pve-v3.sh | 2 +- scripts/global/repository_policy.py | 60 ++++- scripts/global/update-pbs.sh | 4 +- scripts/menus/menu_post_install.sh | 10 +- scripts/post_install/auto_post_install.sh | 4 +- 7 files changed, 334 insertions(+), 25 deletions(-) create mode 100644 .github/scripts/tests/test_pbs_cli_localization.py diff --git a/.github/scripts/tests/test_pbs_cli_localization.py b/.github/scripts/tests/test_pbs_cli_localization.py new file mode 100644 index 00000000..bda1064b --- /dev/null +++ b/.github/scripts/tests/test_pbs_cli_localization.py @@ -0,0 +1,250 @@ +"""PBS CLI language and no-effect boundaries using real functions, isolated files.""" +import contextlib +import copy +import importlib.util +import io +import json +import os +from pathlib import Path +import subprocess +import tempfile +import unittest +from unittest.mock import patch + +ROOT = Path(__file__).resolve().parents[3] + + +def function(path, name): + text = (ROOT/path).read_text() + start = text.index(name+'() {') + return text[start:text.index('\n}\n', start)+3] + + +def repository(components, suite='trixie'): + return dict(Types=['deb'], URIs=['https://credential.invalid/private'], Suites=[suite], + Components=components.split(), Enabled=True) + + +class RepositoryCliTests(unittest.TestCase): + def run_policy(self, case, language='sk', mode='plan', malformed=None, product='pbs'): + spec = importlib.util.spec_from_file_location('policy_fixture', ROOT/'scripts/global/repository_policy.py') + module = importlib.util.module_from_spec(spec); spec.loader.exec_module(module) + state = dict(files=[dict(path='/etc/apt/sources.list.d/pbs.sources', repositories=[repository('pbs-enterprise')]), + dict(path='/etc/apt/sources.list.d/debian.sources', repositories=[repository('main')])], + errors=[], digest='fixture', **{'standard-repos':[{'handle':'no-subscription'}]}) + status='notfound'; calls=[] + if case=='inventory': state['errors']=['PRIVATE RAW DIAGNOSTIC'] + if case=='status': status='unknown' + if case=='suite': state['files'][0]['repositories'][0]['Suites']=['bookworm'] + if case=='active': status='active'; state['files']=state['files'][1:] + if case=='debian': state['files']=state['files'][:1] + if case=='mixed': state['files'][0]['repositories'][0]['Components'].append('main') + if case=='handle': state['standard-repos']=[] + if case=='preserve': state['files'][0]['repositories'][0]['Components']=['pbs-no-subscription'] + original=copy.deepcopy(state) + def run(args): + calls.append(args) + if args[0]=='proxmox-backup-manager': return json.dumps(dict(status=status, serverid='PRIVATE SERVER ID')) + if 'create' in args: + state['files'][0]['repositories'][0]['Enabled']=False + if case=='partial': state['errors']=['PRIVATE RAW DIAGNOSTIC'] + return '' + if 'set' in args: + state['files'].append(dict(path='/etc/apt/sources.list.d/public.sources',repositories=[repository('pbs-no-subscription')])) + return '' + return json.dumps(state) + with tempfile.TemporaryDirectory(prefix='pbs-policy-i18n-') as folder: + module.BASE=Path(folder) + (module.BASE/'lang').mkdir() + (module.BASE/'config.json').write_text(json.dumps({'language':language})) + catalog=(ROOT/'lang/sk.json').read_text() if malformed is None else malformed + (module.BASE/'lang/sk.json').write_text(catalog) + stdout,stderr=io.StringIO(),io.StringIO() + suite='sid' if case=='unsupported' else 'trixie' + with patch.object(module,'run',side_effect=run), patch.object(module.sys,'argv',['repository_policy.py',mode,suite,product]), contextlib.redirect_stdout(stdout),contextlib.redirect_stderr(stderr): + result=module.main() + return result,stdout.getvalue(),stderr.getvalue(),calls,state,original + + def test_pbs_errors_are_localized_without_changing_decisions_or_exposing_raw_data(self): + expectations={'inventory':'Zoznam repozitárov APT', 'status':'Stav predplatného', + 'suite':'iné vydanie', 'active':'aktívne predplatné', 'debian':'základný repozitár Debianu', + 'mixed':'spoločný záznam APT', 'handle':'no-subscription', 'unsupported':'Nepodporované vydanie', + 'partial':'stav repozitárov je neznámy'} + for case,expected in expectations.items(): + with self.subTest(case=case): + result,out,error,calls,state,original=self.run_policy(case,mode='apply' if case=='partial' else 'plan') + self.assertEqual(result,1); self.assertEqual(out,''); self.assertIn(expected,error) + self.assertNotIn('PRIVATE',error); self.assertNotIn('credential.invalid',error) + writes=[c for c in calls if 'create' in c or 'set' in c] + if case=='partial': + self.assertEqual(len(writes),1) + self.assertFalse(state['files'][0]['repositories'][0]['Enabled']) + self.assertIn('Automatický návrat',error) + else: + self.assertEqual(writes,[]); self.assertEqual(state,original) + if case=='unsupported': self.assertEqual(calls,[]) + + def test_english_error_and_stdout_tokens_remain_compatible(self): + en=self.run_policy('unsupported',language='en') + self.assertEqual(en[2],'Unsupported Proxmox suite; no repository changed.\n') + pve=self.run_policy('unsupported',product='pve') + self.assertEqual(pve[2],en[2]) + for mode,expected in [('plan','offer\n'),('apply','changed\n')]: + result,out,error,*_=self.run_policy('offer',mode=mode) + self.assertEqual((result,out,error),(0,expected,'')) + result,out,error,calls,*_=self.run_policy('preserve',mode='apply') + self.assertEqual((result,out,error),(0,'preserve\n','')) + self.assertFalse(any(c[0]=='proxmox-backup-manager' or 'set' in c or 'create' in c for c in calls)) + + def test_bad_or_missing_translation_falls_back_to_original_english(self): + for catalog in ('{}','not json','[]',json.dumps({'Unsupported Proxmox suite; no repository changed.':42}), + json.dumps({'Unsupported Proxmox suite; no repository changed.':'{unknown}'})): + with self.subTest(catalog=catalog): + result,out,error,calls,*_=self.run_policy('unsupported',malformed=catalog) + self.assertEqual(result,1);self.assertEqual(out,'');self.assertEqual(calls,[]) + self.assertEqual(error,'Unsupported Proxmox suite; no repository changed.\n') + + def test_translation_cannot_drop_actual_repository_details(self): + spec=importlib.util.spec_from_file_location('policy_format_fixture',ROOT/'scripts/global/repository_policy.py') + module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module) + with tempfile.TemporaryDirectory(prefix='pbs-policy-format-') as folder: + module.BASE=Path(folder);(module.BASE/'lang').mkdir() + (module.BASE/'config.json').write_text('{"language":"sk"}') + error=module.PolicyError('Inspect {ui}; no repository changed.',ui='Administration > Repositories') + for invalid in ('Skontrolujte repozitáre.','{ui.__class__}','{other}','{ui:.0}','{ui!r}'): + (module.BASE/'lang/sk.json').write_text(json.dumps({error.template:invalid})) + self.assertEqual(module.cli_error(error,'pbs'),str(error)) + for invalid in ('not json','[]','{"language":"../../private"}'): + (module.BASE/'config.json').write_text(invalid) + self.assertEqual(module.cli_error(error,'pbs'),str(error)) + + +UPDATE_HARNESS=r''' +BASE_DIR="$FIXTURE" +LANG_DIR="$REPO/lang" +SCRIPT_TITLE='fixture' +TAB='' BGN='' CL='' GN='' BL='' +mkdir -p "$FIXTURE/log" "$FIXTURE/tmp" "$FIXTURE/boot" "$FIXTURE/run" +: > "$FIXTURE/calls" +record() { printf '%s\n' "$*" >> "$FIXTURE/calls"; } +pmx_journal_context() { :; } +pmx_is_pbs() { [[ "$CASE" != pve ]]; } +pmx_platform_field() { [[ "$1" != supported ]] && printf '%s' "$SUITE" || printf true; } +ensure_pbs_repositories() { record repository-plan; return 0; } +dpkg-query() { [[ "$SUITE" == bookworm ]] && printf 3.4.0 || printf 4.0.1; } +apt-cache() { record apt-cache "$@"; printf ' Candidate: 4.0.2\n'; } +df() { printf 'Filesystem 1K-blocks Used Available Use%% Mounted\nfixture 10000 1000 %s 10%% /\n' "$([[ "$CASE" == disk ]] && echo 512000 || echo 4096000)"; } +apt-get() { record apt-get "$@"; [[ "$CASE" != refresh ]]; } +apt() { + record apt "$@" + if [[ "$*" == 'list --upgradable' ]]; then + printf 'Listing...\n' + [[ "$CASE" == current ]] || printf 'fixture/trixie 1.2 amd64 [upgradable from: 1.1]\n' + return 0 + fi + [[ "$CASE" != failure ]] +} +fixture_env() { "$@"; } +APT_ENV='fixture_env' +pmx_format_upgradable() { cat; } +pmx_record_execution() { record journal "$1"; } +pmx_dpkg_changes_since() { :; } +msg_error() { printf '%s\n' "$*"; } +msg_ok() { printf '%s\n' "$*"; } +msg_info2() { printf '%s\n' "$*"; } +msg_warn() { printf '%s\n' "$*"; } +msg_title() { :; } +clear() { :; } +show_proxmenux_logo() { :; } +whiptail() { printf '%s\n' "$*" >> "$FIXTURE/dialog"; [[ "$CASE" != cancel ]]; } +uname() { printf old-kernel; } +touch "$FIXTURE/boot/vmlinuz-new-kernel" +''' + + +class UpdateCliTests(unittest.TestCase): + def run_update(self,case,language='sk',suite='trixie'): + with tempfile.TemporaryDirectory(prefix='pbs-update-i18n-') as folder: + body=function('scripts/global/update-pbs.sh','update_pbs') + for old,new in [('/var/log/',folder+'/log/'),('/tmp/proxmenux_screen_capture_',folder+'/tmp/proxmenux_screen_capture_'),('/var/run/',folder+'/run/'),('/boot/',folder+'/boot/')]: body=body.replace(old,new) + script=UPDATE_HARNESS+function('scripts/utils.sh','translate')+body+'\nupdate_pbs\n' + result=subprocess.run(['bash','-c',script],capture_output=True,text=True,timeout=15, + env={**os.environ,'REPO':str(ROOT),'FIXTURE':folder,'CASE':case,'LANGUAGE':language,'SUITE':suite}) + files={str(p.relative_to(folder)):p.read_text() for p in Path(folder).rglob('*') if p.is_file()} + return result,files + + def test_pve_guard_cannot_reach_repository_or_apt_calls(self): + result,files=self.run_update('pve') + self.assertEqual(result.returncode,1,result.stderr) + self.assertEqual(files['calls'],'') + self.assertFalse(any(name.startswith('log/') for name in files)) + + def test_disk_and_upgrade_errors_keep_their_actual_parameters(self): + result,files=self.run_update('disk') + self.assertEqual(result.returncode,1,result.stderr) + self.assertIn('Nedostatok miesta na disku. Dostupné:',result.stdout) + self.assertIn('500MB',result.stdout) + self.assertNotIn('apt',files['calls']) + result,files=self.run_update('failure') + self.assertEqual(result.returncode,1,result.stderr) + self.assertIn('Aktualizácia systému zlyhala. Skontrolujte denník:',result.stdout) + self.assertIn('/log/proxmox-update-',result.stdout) + self.assertNotIn('autoremove',files['calls']) + + def test_update_status_cancel_refresh_error_and_reboot_notice(self): + for suite in ('bookworm','trixie'): + for case in ('current','cancel','refresh','success'): + with self.subTest(suite=suite,case=case): + result,files=self.run_update(case,suite=suite) + self.assertEqual(result.returncode,1 if case=='refresh' else 0,result.stderr) + if case!='success': self.assertNotIn('full-upgrade',files['calls']) + if case=='success': self.assertIn('reštartujte server',result.stdout) + if case in ('current','cancel'): self.assertIn('Aktuálna verzia PBS',files['dialog']) + + +class DialogTests(unittest.TestCase): + def test_automated_confirmation_and_banner_cancel_do_not_run_actions(self): + cases=[('scripts/menus/menu_post_install.sh','confirm_automated_script', + ['repozitár no-subscription','upozornenie na predplatné','pamäť','jadro','sieť']), + ('scripts/post_install/auto_post_install.sh','remove_subscription_banner', + ['Proxmox Backup Server – odstránenie upozornenia na predplatné'])] + for path,name,expected in cases: + with tempfile.TemporaryDirectory(prefix='pbs-dialog-') as folder: + script=function('scripts/utils.sh','translate')+function(path,name)+r''' +PMX_ON_PBS=true +LOCAL_SCRIPTS='/fixture/scripts' +clear() { :; }; sleep() { :; }; stop_spinner() { :; } +msg_warn() { printf '%s\n' "$*"; } +bash() { printf forbidden >> "$CAPTURE/actions"; } +register_tool() { printf forbidden >> "$CAPTURE/actions"; } +dialog() { printf '%s\n' "$*" >> "$CAPTURE/dialog"; return 1; } +whiptail() { dialog "$@"; } +'''+name+'\n' + result=subprocess.run(['bash','-c',script],capture_output=True,text=True,timeout=15, + env={**os.environ,'LANGUAGE':'sk','LANG_DIR':str(ROOT/'lang'),'CAPTURE':folder}) + self.assertEqual(result.returncode,1 if name=='remove_subscription_banner' else 0,result.stderr) + dialog=(Path(folder)/'dialog').read_text() + for text in expected:self.assertIn(text,dialog) + self.assertFalse((Path(folder)/'actions').exists()) + if name=='remove_subscription_banner':self.assertIn('bolo zrušené používateľom',result.stdout) + + def test_community_labels_preserve_dispatch_names_and_translate_display(self): + script=function('scripts/utils.sh','translate')+function('scripts/menus/menu_post_install.sh','post_install_label')+'\npost_install_label "$1"\n' + for text,expected in [('Proxmox Backup Server Post Install','nastavenie po inštalácii'), + ('Proxmox Backup Server Microcode','mikrokód procesora')]: + result=subprocess.run(['bash','-c',script,'bash',text],capture_output=True,text=True,timeout=15, + env={**os.environ,'LANGUAGE':'sk','LANG_DIR':str(ROOT/'lang')}) + self.assertEqual(result.returncode,0,result.stderr);self.assertIn(expected,result.stdout) + + def test_failed_apt_hook_check_removes_only_fixture_hook_with_localized_warning(self): + with tempfile.TemporaryDirectory(prefix='pbs-hook-') as folder: + script=function('scripts/utils.sh','translate')+function('scripts/global/remove-banner-pve-v3.sh','create_apt_hook')+r''' +apt-config() { return 1; } +msg_warn() { printf '%s\n' "$*"; } +create_apt_hook +''' + result=subprocess.run(['bash','-c',script],capture_output=True,text=True,timeout=15, + env={**os.environ,'LANGUAGE':'sk','LANG_DIR':str(ROOT/'lang'),'APT_HOOK':folder+'/hook'}) + self.assertEqual(result.returncode,0,result.stderr) + self.assertIn('Nesprávna syntax hooku APT',result.stdout) + self.assertFalse((Path(folder)/'hook').exists()) diff --git a/lang/sk.json b/lang/sk.json index 0834b7f4..5e34a7ec 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -1,4 +1,25 @@ { + "APT hook syntax issue, removing...": "Nesprávna syntax hooku APT; hook sa odstráni...", + "Banner removal cancelled by user.": "Odstránenie upozornenia na predplatné bolo zrušené používateľom.", + "Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.": "Nepodarilo sa dokončiť alebo overiť zmenu repozitárov; zmeny môžu byť čiastočné a stav repozitárov je neznámy. Pred ďalším pokusom skontrolujte {ui}. Automatický návrat do pôvodného stavu sa nevykonal.", + "Enterprise shares an APT stanza with other components. Split it in the Proxmox repository UI; no repository changed.": "Enterprise má spoločný záznam APT s ďalšími komponentmi. Rozdeľte ho v rozhraní repozitárov Proxmoxu; žiadny repozitár sa nezmenil.", + "Host has an active subscription but no active {short} repository. Configure its Enterprise source in {ui}; no repository changed.": "Server má aktívne predplatné, ale nemá aktívny repozitár {short}. Nastavte zdroj Enterprise v {ui}; žiadny repozitár sa nezmenil.", + "Insufficient disk space. Available:": "Nedostatok miesta na disku. Dostupné:", + "No active Debian base repository for this suite; configure it in the Proxmox repository UI before continuing.": "Pre toto vydanie nie je aktívny základný repozitár Debianu; pred pokračovaním ho nastavte v rozhraní repozitárov Proxmoxu.", + "Proxmox APT repository inventory is invalid or reports parse errors. Fix sources in {ui}; no repository changed.": "Zoznam repozitárov APT Proxmoxu je neplatný alebo obsahuje chyby načítania. Opravte zdroje v {ui}; žiadny repozitár sa nezmenil.", + "Proxmox Backup Server Microcode": "Proxmox Backup Server – mikrokód procesora", + "Proxmox Backup Server Post Install": "Proxmox Backup Server – nastavenie po inštalácii", + "Proxmox Backup Server Subscription Banner Removal": "Proxmox Backup Server – odstránenie upozornenia na predplatné", + "Proxmox no-subscription standard repository handle unavailable; no repository changed.": "Štandardný repozitár Proxmoxu no-subscription nie je dostupný; žiadny repozitár sa nezmenil.", + "Subscription status is not unambiguously active, notfound or expired. Check proxmox-backup-manager subscription get locally; no repository changed.": "Stav predplatného nie je jednoznačne active, notfound alebo expired. Overte ho lokálne príkazom proxmox-backup-manager subscription get; žiadny repozitár sa nezmenil.", + "System upgrade failed. Check log:": "Aktualizácia systému zlyhala. Skontrolujte denník:", + "This host runs {name} on {suite}, but these {name} repositories are for another suite: {details}. Disable or remove them in {ui} and run the update again; no repository changed.": "Na serveri beží {name} na vydaní {suite}, ale tieto repozitáre {name} sú pre iné vydanie: {details}. Vypnite alebo odstráňte ich v {ui} a spustite aktualizáciu znova; žiadny repozitár sa nezmenil.", + "Unsupported Proxmox suite; no repository changed.": "Nepodporované vydanie Proxmoxu; žiadny repozitár sa nezmenil.", + "kernel": "jadro", + "memory": "pamäť", + "network": "sieť", + "no-subscription repository": "repozitár no-subscription", + "subscription banner": "upozornenie na predplatné", "# Alternative if you prefer screen": "# Alternatíva, ak preferujete screen", "# Recommended: avoids disconnection during upgrade": "# Odporúčané: zabráni odpojeniu počas aktualizácie", "(Checked entries will be removed. Uncheck to keep in VM.)": "(Označené položky sa odstránia. Zrušte označenie, ak ich chcete ponechať vo VM.)", @@ -88,7 +109,7 @@ "A multi-container application is not recreated: its containers are updated together.": "Aplikácia s viacerými kontajnermi sa nevytvorí znova: jej kontajnery sa aktualizujú spoločne.", "A new ProxMenux version is available:": "Je dostupná nová verzia ProxMenux:", "A new kernel is staged for the next boot:": "Na najbližší štart je pripravený nový kernel:", - "A newer kernel is installed. Reboot before the next backup window to use it.": "Je nainštalované novšie jadro. Aby ste ho mohli použiť, reštartujte ho pred ďalším oknom zálohy.", + "A newer kernel is installed. Reboot before the next backup window to use it.": "Je nainštalované novšie jadro. Pred ďalším plánovaným zálohovaním reštartujte server, aby sa použilo.", "A newer version is available:": "Je dostupná novšia verzia:", "A pending operation exists for": "Čaká na operation existuje", "A pending stack assembly already exists; it is not overwritten": "Nevyriešená zostava stohov už existuje; nie je prepísaná", @@ -4190,7 +4211,7 @@ "No active Proxmox storage accepts OCI template archives.": "Žiadne aktívne úložisko Proxmox neakceptuje archívy šablón OCI.", "No active exports or showmount not available": "Nie sú aktívne exporty alebo príkaz showmount nie je dostupný", "No active session": "Žiadna aktívna relácia", - "No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.": "Žiadne aktívne predplatné a žiadne použiteľné úložisko PBS.Neinteraktívny režim nemôže zmeniť zdroje APT;nakonfigurujte ich v Administrácia > Repozitáre.", + "No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.": "Server nemá aktívne predplatné ani použiteľný repozitár PBS. Neinteraktívny režim nemôže meniť zdroje APT; nastavte ich v Administration > Repositories.", "No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Žiadne aktívne predplatné a žiadne použiteľné úložisko PVE. Neinteraktívny režim nemôže zmeniť zdroje APT; nakonfigurujte ich v časti Uzol > Aktualizácie > Úložiská.", "No additional GPU can be added.": "Nie je možné pridať ďalšiu GPU.", "No additional device needs to be added.": "Netreba pridávať žiadne ďalšie zariadenie.", @@ -5281,7 +5302,7 @@ "Repository helper unavailable. NVIDIA installation stopped.": "Pomocník úložiska nie je k dispozícii. Inštalácia NVIDIA sa zastavila.", "Repository policy returned an unexpected result.": "Politika úložiska vrátila neočakávaný výsledok.", "Repository ready.": "Repozitár je pripravený.", - "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.": "Zdroje úložiska sa zmenili, ale obnovenie zoznamu balíkov APT zlyhalo. Prevádzka zastavená;zdroje neboli vrátené späť. Pred pokračovaním skontrolujte Administrácia > Repozitáre a zopakujte apt-get update.", + "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.": "Zdroje repozitárov sa zmenili, ale obnovenie zoznamu balíkov APT zlyhalo. Operácia sa zastavila; pôvodné zdroje sa neobnovili. Pred pokračovaním skontrolujte Administration > Repositories a zopakujte apt-get update.", "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Zdroje úložiska sa zmenili, ale obnovenie zoznamu balíkov APT zlyhalo. Prevádzka zastavená; zdroje neboli vrátené späť. Pred pokračovaním skontrolujte Node > Updates > Repository a zopakujte apt-get update.", "Repository switch declined; no APT source changed.": "Prepnutie úložiska bolo odmietnuté; žiadny zdroj APT sa nezmenil.", "Repository:": "Repozitár:", @@ -7265,7 +7286,7 @@ "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Asistent aktualizácie: Proxmox VE 8 → 9 (Trixie)", "Upgrade canceled by user": "Aktualizácia zrušená používateľom", "Upgrade one node at a time": "Aktualizujte vždy iba jeden uzol naraz", - "Upgrade the system, offering the": "Aktualizujte systém a ponúknite", + "Upgrade the system, offering the": "Aktualizácia systému a prípadný prechod na", "Upgrade to Proxmox VE 9": "Aktualizovať na Proxmox VE 9", "Upgrading NVIDIA utils (Arch)...": "Aktualizujem NVIDIA nástroje (Arch)...", "Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Nahrať šifrovanú kópiu kľúča do PBS, aby ste ju vedeli obnoviť na preinštalovanom hostovi iba pomocou frázy?", diff --git a/scripts/global/remove-banner-pve-v3.sh b/scripts/global/remove-banner-pve-v3.sh index 92df1028..76df07b5 100644 --- a/scripts/global/remove-banner-pve-v3.sh +++ b/scripts/global/remove-banner-pve-v3.sh @@ -312,7 +312,7 @@ EOFAPT # Verify APT hook syntax apt-config dump >/dev/null 2>&1 || { - msg_warn "APT hook syntax issue, removing..." + msg_warn "$(translate "APT hook syntax issue, removing...")" rm -f "$APT_HOOK" } } diff --git a/scripts/global/repository_policy.py b/scripts/global/repository_policy.py index 95f40d68..00829730 100644 --- a/scripts/global/repository_policy.py +++ b/scripts/global/repository_policy.py @@ -7,8 +7,10 @@ repository API responses: they can contain subscription keys or credentials. """ import copy import json +from pathlib import Path import re import subprocess +import string import sys ENDPOINT = '/nodes/localhost/apt/repositories' @@ -26,6 +28,41 @@ PRODUCTS = { 'test': {'pbs-no-subscription', 'pbs-test', 'pbstest'}, 'ui': 'Administration > Repositories', 'ceph': False}, } +BASE = Path(__file__).resolve().parents[2] + + +def N_(text): + """Mark a CLI message without changing the policy's English exception.""" + return text + + +class PolicyError(ValueError): + def __init__(self, template, **params): + self.template = template + self.params = params + super().__init__(template.format(**params)) + + +def cli_error(error, product): + """Localize only PBS CLI diagnostics; policy callers retain English errors.""" + if product != 'pbs' or not isinstance(error, PolicyError): + return str(error) + try: + language = json.loads((BASE / 'config.json').read_text()).get('language', 'en') + if language not in ('sk', 'de', 'es', 'fr', 'it', 'pt', 'sv'): + return str(error) + catalog = json.loads((BASE / 'lang' / f'{language}.json').read_text()) + message = catalog.get(error.template) + if isinstance(message, str) and message.strip(): + fields = lambda text: {name for _, name, _, _ in string.Formatter().parse(text) if name is not None} + if fields(message) != fields(error.template) or any( + spec or conversion for _, _, spec, conversion in string.Formatter().parse(message) + ): + return str(error) + return message.format(**error.params) + except (OSError, ValueError, KeyError, IndexError, TypeError, AttributeError): + pass + return str(error) def run(args): @@ -43,7 +80,7 @@ def subscription_status(product='pve'): except (ValueError, AttributeError): status = None if status not in ('active', 'notfound', 'expired'): - raise ValueError('Subscription status is not unambiguously active, notfound or expired. Check proxmox-backup-manager subscription get locally; no repository changed.') + raise PolicyError(N_('Subscription status is not unambiguously active, notfound or expired. Check proxmox-backup-manager subscription get locally; no repository changed.')) return status # pvesubscription's CLI get printer emits sorted "key: value" lines, # including a secret key and server ID. Only parse the exact status line. @@ -110,12 +147,12 @@ def repository_state(suite, product='pve'): entries.append((file['path'], index, row)) return data, entries except (KeyError, TypeError, ValueError, IndexError) as exc: - raise ValueError(f"Proxmox APT repository inventory is invalid or reports parse errors. Fix sources in {p['ui']}; no repository changed.") from exc + raise PolicyError(N_('Proxmox APT repository inventory is invalid or reports parse errors. Fix sources in {ui}; no repository changed.'), ui=p['ui']) from exc def evaluate(suite, apply=False, product='pve'): if suite not in ('bookworm', 'trixie'): - raise ValueError('Unsupported Proxmox suite; no repository changed.') + raise PolicyError(N_('Unsupported Proxmox suite; no repository changed.')) p = PRODUCTS[product] data, entries = repository_state(suite, product) pve = [] @@ -139,8 +176,8 @@ def evaluate(suite, apply=False, product='pve'): ceph.append((path, index, row)) # Name the stale sources, never their URIs: those can carry credentials. if wrong_pve: - raise ValueError(f"This host runs {p['name']} on {suite}, but these {p['name']} repositories are for another suite: " - f"{'; '.join(wrong_pve)}. Disable or remove them in {p['ui']} and run the update again; no repository changed.") + raise PolicyError(N_('This host runs {name} on {suite}, but these {name} repositories are for another suite: {details}. Disable or remove them in {ui} and run the update again; no repository changed.'), + name=p['name'], suite=suite, details='; '.join(wrong_pve), ui=p['ui']) if wrong_ceph: raise ValueError(f"This host runs Proxmox VE on {suite}, but these Enterprise Ceph repositories are for another suite: " f"{'; '.join(wrong_ceph)}. Correct them in Node > Updates > Repositories before switching; no repository changed.") @@ -152,20 +189,20 @@ def evaluate(suite, apply=False, product='pve'): status = subscription_status(product) # fail closed before any write if status == 'active': if not pve: - raise ValueError(f"Host has an active subscription but no active {p['short']} repository. Configure its Enterprise source in {p['ui']}; no repository changed.") + raise PolicyError(N_('Host has an active subscription but no active {short} repository. Configure its Enterprise source in {ui}; no repository changed.'), short=p['short'], ui=p['ui']) return 'preserve' if not debian: - raise ValueError('No active Debian base repository for this suite; configure it in the Proxmox repository UI before continuing.') + raise PolicyError(N_('No active Debian base repository for this suite; configure it in the Proxmox repository UI before continuing.')) # No active product source or only inaccessible Enterprise. A mixed stanza # cannot be disabled without also disabling an unrelated component. disable = [item for item in pve if p['enterprise'] in item[2]['Components']] + ceph for _, _, row in disable: expected = {p['enterprise']} if p['enterprise'] in row['Components'] else {'enterprise'} if set(row['Components']) != expected: - raise ValueError('Enterprise shares an APT stanza with other components. Split it in the Proxmox repository UI; no repository changed.') + raise PolicyError(N_('Enterprise shares an APT stanza with other components. Split it in the Proxmox repository UI; no repository changed.')) handles = [r.get('handle') for r in data['standard-repos'] if r.get('handle') == 'no-subscription'] if len(handles) != 1: - raise ValueError('Proxmox no-subscription standard repository handle unavailable; no repository changed.') + raise PolicyError(N_('Proxmox no-subscription standard repository handle unavailable; no repository changed.')) if not apply: return 'offer' # Adopt a refreshed digest only after checking the complete expected @@ -192,11 +229,12 @@ def evaluate(suite, apply=False, product='pve'): for path, index, _ in disable for p, i, row in current): raise ValueError('Could not verify the switched repositories; inspect the Proxmox repository UI before retrying.') except ValueError as exc: - raise ValueError(f"Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {p['ui']} before retrying; no automatic rollback was attempted.") from exc + raise PolicyError(N_('Could not complete or verify the repository switch; changes may be partial and repository state is unknown. Inspect {ui} before retrying; no automatic rollback was attempted.'), ui=p['ui']) from exc return 'changed' def main(): + product = 'pve' try: if len(sys.argv) not in (3, 4) or sys.argv[1] not in ('plan', 'apply') \ or (len(sys.argv) == 4 and sys.argv[3] not in PRODUCTS): @@ -204,7 +242,7 @@ def main(): product = sys.argv[3] if len(sys.argv) == 4 else 'pve' print(evaluate(sys.argv[2], apply=sys.argv[1] == 'apply', product=product)) except ValueError as exc: - print(str(exc), file=sys.stderr) + print(cli_error(exc, product), file=sys.stderr) return 1 return 0 diff --git a/scripts/global/update-pbs.sh b/scripts/global/update-pbs.sh index 54574676..80200ba7 100644 --- a/scripts/global/update-pbs.sh +++ b/scripts/global/update-pbs.sh @@ -52,7 +52,7 @@ update_pbs() { local available_space available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}') if [ "$available_space" -lt 1024 ]; then - msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")" + msg_error "$(translate "Insufficient disk space. Available:") ${available_space}MB" return 1 fi @@ -149,7 +149,7 @@ update_pbs() { rm -f "$screen_capture" if [ $upgrade_exit_code -ne 0 ]; then - msg_error "$(translate "System upgrade failed. Check log: $log_file")" + msg_error "$(translate "System upgrade failed. Check log:") $log_file" echo "Finished: $(date -Iseconds) — upgrade failed (exit $upgrade_exit_code)" >> "$log_file" return 1 fi diff --git a/scripts/menus/menu_post_install.sh b/scripts/menus/menu_post_install.sh index d4e63cda..2f9696e6 100644 --- a/scripts/menus/menu_post_install.sh +++ b/scripts/menus/menu_post_install.sh @@ -63,13 +63,13 @@ confirm_automated_script() { if [[ "$PMX_ON_PBS" == true ]]; then script_info+="$(translate "This script will apply the following optimizations and advanced adjustments to your Proxmox Backup Server"):\n\n" - script_info+="• $(translate "Upgrade the system, offering the") \Z4no-subscription repository\Z0 $(translate "when the server has no subscription (you will be asked)")\n" + script_info+="• $(translate "Upgrade the system, offering the") \Z4$(translate "no-subscription repository")\Z0 $(translate "when the server has no subscription (you will be asked)")\n" else script_info+="$(translate "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server"):\n\n" script_info+="• $(translate "Configure") \Z4free repositories\Z0 $(translate "and upgrade the system (disables the enterprise repo)")\n" fi - script_info+="• $(translate "Optionally remove") \Z4subscription banner\Z0 $(translate "from Proxmox web interface (you will be asked)")\n" - script_info+="• $(translate "Optimize") \Z4memory\Z0, \Z4kernel\Z0, $(translate "and") \Z4network\Z0 $(translate "for better performance and stability")\n" + script_info+="• $(translate "Optionally remove") \Z4$(translate "subscription banner")\Z0 $(translate "from Proxmox web interface (you will be asked)")\n" + script_info+="• $(translate "Optimize") \Z4$(translate "memory")\Z0, \Z4$(translate "kernel")\Z0, $(translate "and") \Z4$(translate "network")\Z0 $(translate "for better performance and stability")\n" script_info+="• $(translate "Install and configure") \Z4Log2RAM\Z0 $(translate "(only on SSD/NVMe) to protect your disk")\n" script_info+="• $(translate "Enable") \Z4ZFS autotrim\Z0 $(translate "on SSD/NVMe pools that support discard")\n" script_info+="• $(translate "Improve log rotation and limit log size to save space and extend disk life")\n" @@ -289,10 +289,10 @@ post_install_label() { echo "Proxmox VE Microcode" ;; "Proxmox Backup Server Post Install") - echo "Proxmox Backup Server Post Install" + translate "Proxmox Backup Server Post Install" ;; "Proxmox Backup Server Microcode") - echo "Proxmox Backup Server Microcode" + translate "Proxmox Backup Server Microcode" ;; "Community Scripts") translate "Community Scripts" diff --git a/scripts/post_install/auto_post_install.sh b/scripts/post_install/auto_post_install.sh index e11f131f..1602ad39 100644 --- a/scripts/post_install/auto_post_install.sh +++ b/scripts/post_install/auto_post_install.sh @@ -182,9 +182,9 @@ remove_subscription_banner() { # description: Patch the Proxmox web UI to suppress the subscription dialog and register a successful patch. if [[ "$PMX_ON_PBS" == true ]]; then stop_spinner - if ! whiptail --title "Proxmox Backup Server Subscription Banner Removal" \ + if ! whiptail --title "$(translate "Proxmox Backup Server Subscription Banner Removal")" \ --yesno "$(translate "Do you want to remove the Proxmox subscription banner from the Proxmox Backup Server web interface?")\n\n$(translate "Attention: Removing the subscription banner may cause issues in the web interface after a future update.")\n\n$(translate "If this happens, you can restore the backup from the 'Subscription Banner Removal' option in 'Uninstall optimizations'.")\n\n$(translate "Are you sure you want to continue?")" 14 75; then - msg_warn "Banner removal cancelled by user." + msg_warn "$(translate "Banner removal cancelled by user.")" return 1 fi if ! bash "$LOCAL_SCRIPTS/global/remove-banner-pve-v3.sh"; then From 43041846eb624dd1c72230f895dabc2c5a424721 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:30:29 +0000 Subject: [PATCH 08/27] i18n: complete Slovak custom OCI image and registry dialogs --- .../test_oci_custom_registry_localization.py | 268 ++++++++++++++++++ .github/scripts/tests/test_oci_wording.py | 2 +- lang/sk.json | 222 ++++++++------- oci/src/proxmenux_oci/custom.py | 64 ++++- 4 files changed, 442 insertions(+), 114 deletions(-) create mode 100644 .github/scripts/tests/test_oci_custom_registry_localization.py diff --git a/.github/scripts/tests/test_oci_custom_registry_localization.py b/.github/scripts/tests/test_oci_custom_registry_localization.py new file mode 100644 index 00000000..10d9760b --- /dev/null +++ b/.github/scripts/tests/test_oci_custom_registry_localization.py @@ -0,0 +1,268 @@ +"""Exercise the native custom-image and registry UI with the real EN/SK lookup.""" +import json +from pathlib import Path +import runpy +import sys +import tempfile +from types import SimpleNamespace +import unittest +from unittest.mock import MagicMock, patch + +ROOT = Path(__file__).resolve().parents[3] +sys.path.insert(0, str(ROOT / 'oci/src')) +from proxmenux_oci import custom, i18n, registry_accounts as accounts, registry_auth as auth +from proxmenux_oci.ui import UserCancelled + +COMPOSE = 'services:\n app:\n image: ghcr.io/owner/app:latest\n' + + +class UI: + def __init__(self, choices=(), answers=(), confirms=()): + self.choices = iter(choices) + self.answers = iter(answers) + self.confirms = iter(confirms) + self.seen = [] + + def choose(self, text, options, default=None, **kwargs): + self.seen.append((text, options, kwargs)) + return next(self.choices) + + def ask(self, text, default=None, **kwargs): + self.seen.append(text) + return next(self.answers) + + def password(self, text): + self.seen.append(text) + raise UserCancelled(text) + + def confirm(self, text, default=False, **kwargs): + self.seen.append((text, default)) + return next(self.confirms) + + def review(self, text, title=None, **kwargs): + self.seen.append((text, title, kwargs)) + return False + + def message(self, text, title=None): + self.seen.append((text, title)) + + info = message + + +class CustomRegistryLocalizationTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + self.base = Path(self.temp.name) + (self.base / 'lang').mkdir() + (self.base / 'config.json').write_text('{"language":"sk"}') + (self.base / 'lang/sk.json').write_bytes((ROOT / 'lang/sk.json').read_bytes()) + for name, value in [('BASE_DIR', self.base), ('_language', None), ('_cache', None)]: + item = patch.object(i18n, name, value) + item.start() + self.addCleanup(item.stop) + # Any unmocked subprocess, network fetch or installation is a failure. + self.run = self.guard(custom.subprocess, 'run') + self.network = self.guard(custom.urllib.request, 'urlopen') + self.install = self.guard(custom, 'install_template') + + def guard(self, module, name): + item = patch.object(module, name, side_effect=AssertionError('Forbidden effect: ' + name)) + mock = item.start() + self.addCleanup(item.stop) + return mock + + def no_effects(self): + self.run.assert_not_called() + self.network.assert_not_called() + self.install.assert_not_called() + + def manager(self): + return SimpleNamespace(path=Path('/fixture/auth.json'), + accounts=MagicMock(return_value=[{'registry': 'ghcr.io', 'account': 'owner', + 'method': 'Používateľské meno / token', 'status': 'Saved'}]), + login=MagicMock(), logout=MagicMock()) + + def menu(self, ui, manager): + with patch.object(accounts, 'require_root'): + accounts.registry_accounts_menu(ui, auth=manager) + + def test_dynamic_blockers_are_extracted_and_rendered_in_slovak(self): + expected = { + 'multi-service-compose': 'definuje viac než jeden obraz', + 'native-multi-lxc-orchestrator-not-yet-implemented': 'definuje viac než jeden obraz', + 'top-level-configs': 'používa nastavenia Compose configs, ktoré tu nemajú ekvivalent', + 'devices-format': 'požadované zariadenia sú zadané v nepodporovanom formáte', + 'healthcheck-format': 'kontrola stavu je zadaná v nepodporovanom formáte', + 'stop-grace-period-format': 'časový limit zastavenia je zadaný v nepodporovanom formáte', + 'shm-size-format': 'veľkosť zdieľanej pamäte je zadaná v nepodporovanom formáte', + 'ulimits-format-or-resource': 'požadované limity systémových prostriedkov nemožno uplatniť', + 'mem-limit-format-or-below-proxmox-minimum': 'požadovaný limit pamäte nemožno uplatniť', + 'mem-limit-deploy-consistency-review': 'požaduje dva rozdielne limity pamäte', + } + extractor = runpy.run_path(str(ROOT / '.github/scripts/build_translation_cache.py')) + extracted = extractor['extract_python_texts']([ROOT / 'oci/src']) + self.assertTrue(set(custom.BLOCKER_TEXTS.values()) <= set(extracted)) + for code, message in expected.items(): + with self.subTest(code=code): + self.assertEqual(custom.blocker_text('service:app:' + code), message) + self.assertEqual(custom.blocker_text('unrecognized:raw-code'), 'unrecognized:raw-code') + self.no_effects() + + def test_cancelled_source_menu_is_translated_without_reading_an_image(self): + ui = UI([None]) + with self.assertRaises(UserCancelled) as caught: + custom.explore(ui) + self.assertEqual(str(caught.exception), 'Výber obrazu bol zrušený') + self.assertIn('Obraz, ktorý nie je v katalógu', repr(ui.seen)) + self.assertIn('Len odkaz na obraz bez súboru Compose', repr(ui.seen)) + self.no_effects() + + def test_invalid_compose_inputs_reach_localized_error_before_registry_or_install(self): + cases = [ + ('services: {}', 'Súbor Compose neobsahuje žiadnu službu'), + ('services: [', 'Súbor Compose nie je platný YAML:'), + (COMPOSE + ' environment: 42\n', 'Položka environment'), + (COMPOSE + ' volumes: {}\n', 'Položka volumes'), + (COMPOSE + ' ports: invalid\n', 'Položka ports'), + (COMPOSE + ' ports: [invalid]\n', 'Neplatný port'), + (COMPOSE + ' volumes: [relative:relative]\n', 'Neplatná cesta dátového úložiska'), + (COMPOSE + 'x-casaos:\n main: missing\n', 'Položka x-casaos.main'), + ] + for text, expected in cases: + with self.subTest(text=text), patch.object(custom, 'read_definition', return_value=(text, 'fixture')), \ + self.assertRaises(custom.ConversionError) as caught: + custom.explore(UI()) + self.assertIn(expected, str(caught.exception)) + self.no_effects() + + def test_converter_raw_contract_and_english_ui_are_preserved(self): + bad = COMPOSE + ' ports: [invalid]\n' + with self.assertRaises(custom.ConversionError) as raw: + custom.template_from_compose(bad) + self.assertEqual(str(raw.exception), "Puerto no valido: 'invalid'") + with patch.object(i18n, '_language', 'en'), \ + patch.object(custom, 'read_definition', return_value=(bad, 'fixture')), \ + self.assertRaises(custom.ConversionError) as ui_error: + custom.explore(UI()) + self.assertEqual(str(ui_error.exception), str(raw.exception)) + self.no_effects() + + def test_conversion_message_retains_literal_diagnostic_data_and_unknown_fallback(self): + detail = " {'target': '{literal}\\á'}" + message = custom.conversion_message(custom.ConversionError('Ruta de volumen no valida:' + detail)) + self.assertEqual(message, 'Neplatná cesta dátového úložiska v súbore Compose:' + detail) + unknown = 'unknown {literal}' + self.assertEqual(custom.conversion_message(custom.ConversionError(unknown)), unknown) + self.no_effects() + + def test_all_legacy_converter_diagnostics_have_slovak_ui_and_unchanged_english(self): + messages = [ + 'environment must be a list or a mapping', 'volumes must be a list', 'ports must be a list', + 'The CasaOS Compose file has no services', 'The CasaOS document is not a Compose mapping', + 'The Compose file has no x-casaos metadata', 'x-casaos.main no identifica un servicio valido', + 'The main CasaOS service declares no image', 'Ruta de volumen no valida: /data', + 'Puerto no valido: invalid', 'Cannot normalise the CasaOS identifier: ???', + 'Docker Compose CasaOS no valido: fixture', + ] + for text in messages: + with self.subTest(text=text): + self.assertNotEqual(custom.conversion_message(custom.ConversionError(text)), text) + with patch.object(i18n, '_language', 'en'): + self.assertEqual(custom.conversion_message(custom.ConversionError(text)), text) + self.no_effects() + + def test_custom_review_cancel_keeps_installation_disabled(self): + ui = UI() + with patch.object(custom, 'read_definition', return_value=(COMPOSE, 'vloženého súboru Compose')), \ + patch.object(custom, 'registry_report', return_value=(True, 'Dostupný obraz')): + custom.explore(ui) + self.assertIn('Nainštalovať tento obraz?', repr(ui.seen)) + self.assertIn('Obraz:', repr(ui.seen)) + self.assertIn('ghcr.io/owner/app:latest', repr(ui.seen)) + self.no_effects() + + def test_unavailable_image_explains_registry_accounts_without_authentication_or_install(self): + ui = UI() + with patch.object(custom, 'read_definition', return_value=(COMPOSE, 'fixture')), \ + patch.object(custom, 'registry_report', return_value=(False, 'Nedostupný obraz')), \ + patch.object(custom, '_registry_refused', return_value=False), \ + patch.object(accounts, 'offer_sign_in') as offer: + custom.explore(ui) + self.assertIn('prihláste cez Účty registrov', repr(ui.seen)) + offer.assert_not_called() + self.no_effects() + + def test_registry_metadata_dialog_uses_image_and_digest_and_retains_values(self): + manager = self.manager() + ui = UI(['account-0', 'inspect', None], ['ghcr.io/owner/app:latest']) + metadata = dict(architecture='amd64', version='{literal}', size=1024, digest='sha256:' + 'a'*64) + with patch.object(accounts, 'inspect_registry_image', return_value=metadata) as inspect: + self.menu(ui, manager) + inspect.assert_called_once_with('ghcr.io/owner/app:latest') + rendered = repr(ui.seen) + for value in ('Prístup k obrazu bol potvrdený.', 'Digest: sha256:', '{literal}', 'Účty registrov'): + self.assertIn(value, rendered) + self.assertNotIn('obráz', rendered) + manager.login.assert_not_called() + manager.logout.assert_not_called() + self.no_effects() + + def test_credential_help_retains_permissions_and_no_encryption_claim(self): + manager = self.manager() + ui = UI(['storage', None]) + self.menu(ui, manager) + rendered = repr(ui.seen) + for value in ('root', '700', '600', 'base64', 'nie sú šifrované', 'každý uzol klastra', '/fixture/auth.json'): + self.assertIn(value, rendered) + manager.login.assert_not_called() + manager.logout.assert_not_called() + self.no_effects() + + def test_cancelled_password_and_declined_logout_do_not_change_accounts(self): + for ui in (UI(['add', None], ['ghcr.io', 'owner']), UI(['account-0', 'logout', None], confirms=[False])): + manager = self.manager() + self.menu(ui, manager) + manager.login.assert_not_called() + manager.logout.assert_not_called() + self.no_effects() + + def test_mismatched_registry_is_rejected_before_download(self): + manager = self.manager() + ui = UI(['account-0', 'download', None], ['quay.io/owner/app:latest']) + with patch.object(accounts, 'download_registry_image') as download: + self.menu(ui, manager) + download.assert_not_called() + self.assertIn('Zadajte obraz z tohto registra.', repr(ui.seen)) + self.no_effects() + + def test_declined_private_image_signin_does_not_even_construct_auth_manager(self): + ui = UI(confirms=[False]) + with patch.object(accounts, 'RegistryAuth') as manager, patch.object(accounts, 'require_root') as root: + self.assertFalse(accounts.offer_sign_in(ui, 'ghcr.io/owner/app:latest')) + manager.assert_not_called() + root.assert_not_called() + self.assertIn('Prihlásiť sa teraz?', repr(ui.seen)) + self.no_effects() + + def test_registry_error_classes_hide_untrusted_remote_data_in_both_languages(self): + cases = [('status code: 429', 'obmedzuje počet požiadaviek'), + ('requested access to the resource is denied', 'odmietol prístup'), + ('manifest unknown', 'Obraz alebo tag'), + ('unsupported manifest', 'metadáta obrazu'), + ('docker-credential-helper', 'správu prihlasovacích údajov'), + ('unknown failure', 'Obraz sa nepodarilo načítať')] + for language in ('sk', 'en'): + for error, expected in cases: + with self.subTest(language=language, error=error), patch.object(i18n, '_language', language): + message = auth.registry_error('registry.test:5000', error + ' REMOTE_PRIVATE_VALUE') + self.assertNotIn('REMOTE_PRIVATE_VALUE', message) + if language == 'sk': + self.assertIn(expected, message) + if 'credential' not in error: + self.assertIn('registry.test:5000', message) + self.no_effects() + + +if __name__ == '__main__': + unittest.main() diff --git a/.github/scripts/tests/test_oci_wording.py b/.github/scripts/tests/test_oci_wording.py index c38e3671..76499b5c 100644 --- a/.github/scripts/tests/test_oci_wording.py +++ b/.github/scripts/tests/test_oci_wording.py @@ -121,7 +121,7 @@ class OciWordingTests(TestCase): def test_failed_registry_inspection_points_to_registry_accounts(self): ui = SimpleNamespace(message=Mock()) - fn = function(CUSTOM, 'explore', read_definition=lambda _: ('definition', 'source'), + fn = function(CUSTOM, '_explore', read_definition=lambda _: ('definition', 'source'), ignored_settings=lambda _: [], template_from_compose=lambda _: { 'container_contract': {'image': {'reference': 'example:latest'}}}, describe=lambda _: 'SUMMARY', registry_report=lambda _: (False, 'INSPECTION FAILED'), diff --git a/lang/sk.json b/lang/sk.json index fe1f96ce..ee634fac 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -196,7 +196,7 @@ "Access bridge for Tandoor": "Prístupový mostík pre Tandoor", "Access could not be granted completely; the application may be unable to write to:": "Prístup nebolo možné úplne udeliť; aplikácia možno nebude môcť zapisovať do:", "Access could not be granted; the application cannot write to:": "Prístup nebolo možné udeliť; aplikácia nemôže písať na:", - "Access denied by {registry}. Check the image name and sign in from Registry accounts.": "Prístup odmietnutý {registry}. Skontrolujte názov obrázka a prihláste sa z účtov Registry.", + "Access denied by {registry}. Check the image name and sign in from Registry accounts.": "Register {registry} odmietol prístup. Skontrolujte názov obrazu a prihláste sa cez Účty registrov.", "Access granted to the application on:": "Prístup udelený aplikácii dňa:", "Access password of the web interface (optional)": "Heslo na prístup do webového rozhrania (voliteľné)", "Access profile:": "Profil prístupu:", @@ -293,7 +293,7 @@ "Address Healthchecks will be opened at (for example http://192.168.1.50:8000)": "Adresa, na ktorej bude Healthchecks dostupný (napríklad http://192.168.1.50:8000)", "Address Speedtest Tracker will be opened at (for example http://192.168.1.50)": "Adresa, na ktorej bude Speedtest Tracker dostupný (napríklad http://192.168.1.50)", "Address of Redis or Valkey (for example redis://192.168.1.20:6379/1)": "Adresa služby Redis alebo Valkey (napríklad redis://192.168.1.20:6379/1)", - "Address of the Compose file": "Adresa súboru", + "Address of the Compose file": "Adresa súboru Compose", "Address of the MariaDB server": "Adresa servera MariaDB", "Address of the NetBird management server": "Adresa riadiaceho servera NetBird", "Address of the PostgreSQL server": "Adresa servera PostgreSQL", @@ -646,7 +646,7 @@ "Beta version:": "Beta verzia:", "Binary not found in extracted content.": "V rozbalenom obsahu sa nenašiel spustiteľný súbor.", "Bind mount added:": "Bind mount pridaný:", - "Bind mounts are not included in vzdump": "Vzadump nie sú zahrnuté svorníky", + "Bind mounts are not included in vzdump": "Pripojené adresáre hostiteľa nie sú zahrnuté v zálohe vzdump", "Bitcoin Knots can be used as a desktop client for regular payments or as a full node server utility for merchants and other payment services.": "Bitcoin Knots môže byť použitý ako desktop klient pre pravidelné platby alebo ako kompletný uzol server nástroj pre obchodníkov a iné platobné služby.", "Blacklist nouveau driver": "Pridá ovládač nouveau na blacklist", "Blacklisting GPU host drivers...": "Pridávam host ovládače GPU na blacklist...", @@ -754,7 +754,7 @@ "Cannot resolve the container home directory:": "Nedá sa vyriešiť domovský adresár kontajnera:", "Cannot validate credentials - no shares available for testing.": "Prihlasovacie údaje sa nedajú overiť - nie sú dostupné žiadne zdieľania na test.", "Cannot verify the reused disk:": "Nie je možné overiť znovu použitý disk:", - "Cannot write the image archive to {storage}. Check storage access and mount status.": "Nie je možné zapísať archív obrázkov do {storage}. Skontrolujte prístup k úložisku a stav pripojenia.", + "Cannot write the image archive to {storage}. Check storage access and mount status.": "Archív obrazu sa nedá zapísať do úložiska {storage}. Skontrolujte prístup k úložisku a stav jeho pripojenia.", "Capabilities cannot be kept and all dropped at the same time": "Nie je možné udržať si schopnosti a zároveň ich znížiť.", "Catalog options": "Možnosti katalógu", "Category": "Kategória", @@ -816,7 +816,7 @@ "Check Samba Status": "Skontrolovať stav Samby", "Check VM disk/PCI slots": "Skontrolovať diskové/PCI sloty VM", "Check VM passthrough settings": "Skontrolovať nastavenie VM passthrough", - "Check access to an image": "Kontrola prístupu k obrázku", + "Check access to an image": "Overiť prístup k obrazu", "Check connectivity with another host": "Skontrolovať spojenie s iným hostom", "Check container mount points": "Skontrolovať body pripojenia kontajnera", "Check for updates": "Vyhľadať aktualizácie", @@ -830,7 +830,7 @@ "Check that:": "Skontrolujte, že:", "Check the error messages above for details.": "Podrobnosti nájdete v chybových hláškach vyššie.", "Check the image reference and registry access. If the registry is unavailable, try again later. A private registry needs credentials, which are not supported yet.": "Skontrolujte odkaz na obraz a prístup k registru. Ak register nie je dostupný, skúste to neskôr. Súkromný register vyžaduje prihlasovacie údaje, ktoré zatiaľ nie sú podporované.", - "Check the image reference and registry access. If the registry is unavailable, try again later. For a private image, sign in from Registry accounts in the OCI menu.": "Skontrolujte odkaz na obrázok a prístup do registra. Ak register nie je dostupný, skúste to znova neskôr. Pre súkromný obrázok sa prihláste z účtov Registry v ponuke OCI.", + "Check the image reference and registry access. If the registry is unavailable, try again later. For a private image, sign in from Registry accounts in the OCI menu.": "Skontrolujte odkaz na obraz a prístup do registra. Ak register nie je dostupný, skúste to znova neskôr. K súkromnému obrazu sa prihláste cez Účty registrov v ponuke OCI.", "Check the service status manually if needed.": "V prípade potreby skontrolujte stav služieb ručne.", "Checking MOTD configuration...": "Kontrolujem nastavenie MOTD...", "Checking NVIDIA driver status with nvidia-smi": "Kontrolujem stav ovládača NVIDIA pomocou nvidia-smi", @@ -845,7 +845,7 @@ "Checking free space in /var/cache/apt/archives...": "Kontrolujem voľné miesto v /var/cache/apt/archives...", "Checking if system disk is SSD or M.2...": "Kontrolujem, či je systémový disk SSD alebo M.2...", "Checking if the server belongs to OVH...": "Kontrolujem, či server patrí pod OVH...", - "Checking image access and metadata. Please wait...": "Kontrola prístupu k obrázku a metadát. Čakajte prosím...", + "Checking image access and metadata. Please wait...": "Overuje sa prístup k obrazu a jeho metadáta. Čakajte, prosím...", "Checking kernel headers and build tools...": "Kontrolujem kernel headers a nástroje na zostavenie...", "Checking remaining interfaces": "Kontrolujem zostávajúce rozhrania", "Checking that the container keeps running...": "Kontrola, či kontajner beží...", @@ -921,7 +921,7 @@ "Choose iperf3 mode:": "Vyberte režim iperf3:", "Choose options to configure:": "Vyberte možnosti, ktoré chcete nastaviť:", "Choose specific VMs/CTs": "Vyberte konkrétne VM/CT", - "Choose storage for the downloaded image.": "Vyberte úložisko pre stiahnutý obrázok.", + "Choose storage for the downloaded image.": "Vyberte úložisko pre stiahnutý obraz.", "Choose the driver version for Coral M.2:": "Vyberte verziu ovládača pre Coral M.2:", "Choose the filesystem for the new partition:": "Vyberte súborový systém pre nový oddiel:", "Choose the release channel to use:": "Vyberte vetvu vydania, ktorú chcete používať:", @@ -1017,8 +1017,8 @@ "Component:": "Súčasť:", "Components to uninstall (manual for now):": "Súčasti na odinštalovanie (zatiaľ ručne):", "Compose capabilities validated in the LXC user namespace:": "Skladanie schopností validovaných v LXC užívateľskom priestore:", - "Compose file of the application": "Zostaviť súbor žiadosti", - "Compose file of this host": "Zostaviť súbor tohto hostiteľa", + "Compose file of the application": "Súbor Compose aplikácie", + "Compose file of this host": "súboru Compose z tohto hostiteľa", "Compressed size:": "Veľkosť po kompresii:", "Compressing": "Komprimujem", "Compression": "Kompresia", @@ -1396,14 +1396,14 @@ "Could not read the CUDA compute capability": "Nepodarilo sa prečítať výpočtovú schopnosť CUDA", "Could not read the NVIDIA driver version": "Nepodarilo sa prečítať verziu ovládača NVIDIA", "Could not read the host firewall rules": "Pravidlá firewallu hostiteľa sa nepodarilo načítať", - "Could not read the image from {registry}. Check the reference and sign in from Registry accounts.": "Nepodarilo sa prečítať obrázok z {registry}. Skontrolujte referenciu a prihláste sa z účtov Registra.", + "Could not read the image from {registry}. Check the reference and sign in from Registry accounts.": "Obraz sa nepodarilo načítať z registra {registry}. Skontrolujte odkaz na obraz a prihláste sa cez Účty registrov.", "Could not read this job.": "Túto úlohu sa nepodarilo prečítať.", "Could not release ZRAM pages; existing profile remains active": "Nepodarilo sa uvoľniť stránky ZRAM; existujúci profil zostáva aktívny", "Could not remount automatically. Try manually or check credentials.": "Automatické znovupripojenie zlyhalo. Skúste to ručne alebo skontrolujte prihlasovacie údaje.", "Could not remove VM automatically. Run manually:": "VM sa nepodarilo odstrániť automaticky. Spustite ručne:", "Could not remove ZRAM profile; configuration was restored": "Nepodarilo sa odstrániť profil ZRAM; konfigurácia bola obnovená", "Could not remove ZRAM profile; configuration was restored but swap could not be restarted": "Nepodarilo sa odstrániť profil ZRAM; konfigurácia bola obnovená, ale swap nebolo možné reštartovať", - "Could not remove credentials for {registry}; saved file credentials were not changed.": "Nepodarilo sa odstrániť poverenia pre {registry}; poverenia uloženého súboru neboli zmenené.", + "Could not remove credentials for {registry}; saved file credentials were not changed.": "Prihlasovacie údaje pre {registry} sa nepodarilo odstrániť; údaje uložené v súbore sa nezmenili.", "Could not remove previous DKMS tree at": "Nepodarilo sa odstrániť predchádzajúci DKMS strom v", "Could not reserve a private network for the stack": "Nepodarilo sa rezervovať súkromnú sieť pre stack", "Could not resolve the Compose user:": "Nepodarilo sa vyriešiť používateľa Compose:", @@ -1505,15 +1505,15 @@ "Creating the private network...": "Vytváranie súkromnej siete...", "Creating the temporary data container": "Vytvorenie dočasného dátového kontajnera", "Creative & Design": "Kreativita a dizajn", - "Credential helper": "Pomocník poverení", - "Credential storage and access": "Úložisko poverení a prístup", + "Credential helper": "Pomocný program na správu prihlasovacích údajov", + "Credential storage and access": "Ukladanie prihlasovacích údajov a prístup", "Credentials are correct": "Prihlasovacie údaje sú správne", "Credentials cleared. jwt_secret and API tokens preserved.": "Prihlasovacie údaje sú vymazané. jwt_secret a API tokeny zostali zachované.", "Credentials file created securely.": "Súbor s prihlasovacími údajmi bol bezpečne vytvorený.", "Credentials file removed.": "Súbor s prihlasovacími údajmi bol odstránený.", "Credentials file:": "Súbor s prihlasovacími údajmi:", "Credentials file:\n{path}\n\nOwned by root; directory mode 700 and file mode 600.\nBasic credentials are base64 encoded, not encrypted.\nCredential helpers use their own storage.\n\nUse an access token with permission to pull images.\nConfigure credentials on each node that downloads images.": "Súbor poverení:\n{path}\n\nVo vlastníctve root; režim adresárov 700 a režim súborov 600.\nZákladné poverenia sú zakódované pomocou base64, nie sú zašifrované.\nPomocníci poverení využívajú svoje vlastné úložisko.\n\nPoužite prístupový token s povolením na stiahnutie obrázkov.\nNakonfigurujte poverenia na každom uzle, ktorý sťahuje obrázky.", - "Credentials file:\n{path}\n\nOwned by root; directory mode 700 and file mode 600.\nBasic credentials are base64 encoded, not encrypted.\nCredential helpers use their own storage.\n\nUse an access token with permission to pull images.\nEvery node of the cluster uses the accounts saved here.": "Súbor poverení:\n{path}\n\nVo vlastníctve root; režim adresárov 700 a režim súborov 600.\nZákladné poverenia sú zakódované pomocou base64, nie sú zašifrované.\nPomocníci poverení využívajú svoje vlastné úložisko.\n\nPoužite prístupový token s povolením na stiahnutie obrázkov.\nKaždý uzol klastra používa tu uložené účty.", + "Credentials file:\n{path}\n\nOwned by root; directory mode 700 and file mode 600.\nBasic credentials are base64 encoded, not encrypted.\nCredential helpers use their own storage.\n\nUse an access token with permission to pull images.\nEvery node of the cluster uses the accounts saved here.": "Súbor s prihlasovacími údajmi:\n{path}\n\nVlastníkom je root; adresár má oprávnenia 700 a súbor 600.\nZákladné prihlasovacie údaje sú kódované pomocou base64, nie sú šifrované.\nPomocné programy na správu prihlasovacích údajov používajú vlastné úložisko.\n\nPoužite prístupový token s oprávnením sťahovať obrazy.\nÚčty uložené na tomto mieste používa každý uzol klastra.", "Credentials validated successfully": "Prihlasovacie údaje boli úspešne overené", "Cross-host restore: guest IDs in backup overlap live IDs on target:": "Obnova medzi hostami: ID hostí zo zálohy sa prekrývajú so živými ID na cieli:", "Cross-kernel restore — kernel-tied paths merged, not copied": "Obnova medzi kernelmi - cesty viazané na kernel boli zlúčené, nie skopírované", @@ -1892,7 +1892,7 @@ "Doplarr_rs is a Discord bot for requesting media through *arr backends, written in Rust.": "Doplarr_rs je robot Discord pre žiadanie médií cez arr backends, napísané v Rust.", "Double Commander is a free cross platform open source file manager with two panels side by side. It is inspired by Total Commander and features some new ideas.": "Double Commander je free cross platform open source správca súborov s dvoma panelmi vedľa seba. Je inšpirovaný Total Commander a obsahuje niektoré nové nápady.", "Download Spotify music with album art and metadata": "Stiahnuť Spotify hudbu s albumom a metadátami", - "Download an image to template storage": "Stiahnite si obrázok do úložiska šablón", + "Download an image to template storage": "Stiahnuť obraz do úložiska šablón", "Download an oci-compose from an address": "Stiahnuť oci-compose z adresy", "Download failed for all attempted URLs": "Sťahovanie zlyhalo zo všetkých vyskúšaných adries", "Download its Compose file from an address": "Stiahnuť súbor Compose z URL adresy", @@ -1908,7 +1908,7 @@ "Downloading Proxmology logo...": "Sťahujem logo Proxmology...", "Downloading UUP converter...": "Sťahujem UUP konvertor...", "Downloading amdgpu_top": "Sťahujem amdgpu_top", - "Downloading and verifying the image.\nThis can take a few minutes. Please wait...": "Stiahnutie a overenie obrázka.\nMôže to trvať niekoľko minút. Čakajte prosím...", + "Downloading and verifying the image.\nThis can take a few minutes. Please wait...": "Obraz sa sťahuje a overuje.\nMôže to trvať niekoľko minút. Čakajte, prosím...", "Downloading official installer...": "Sťahujem oficiálny inštalátor...", "Downloading pre-existing encrypted backups from this host will fail unless you kept a copy of the current key.": "Stiahnutie už existujúcich šifrovaných záloh z tohto hosta zlyhá, ak ste si nenechali kópiu aktuálneho kľúča.", "Downloading the image:": "Sťahovanie obrázku:", @@ -2935,25 +2935,25 @@ "If you want HDMI/analog audio inside the VM, select the audio controller(s) to pass through along with the GPU.": "Ak chcete mať vo VM HDMI/analógový zvuk, vyberte aj audio radiče, ktoré sa majú priradiť spolu s GPU.", "If you want to use a physical monitor on the passthrough GPU:": "Ak chcete použiť fyzický monitor na priamo priradenej GPU:", "If your DHCP has a static reservation for the old MAC, update it.": "Ak máte v DHCP statickú rezerváciu pre starú MAC adresu, aktualizujte ju.", - "Image": "Image", + "Image": "Obraz", "Image Source Directory": "Zdrojový priečinok obrazov", - "Image access confirmed.\n\n{reference}\n\nArchitecture: {architecture}\nVersion: {version}\nDownload size: {size}\nDigest: {digest}": "Prístup k obrázku bol potvrdený.\n\n{reference}\n\nArchitektúra: {architecture}\nVerzia: {version}\nVeľkosť na stiahnutie: {size}\nZhrnutie: {digest}", + "Image access confirmed.\n\n{reference}\n\nArchitecture: {architecture}\nVersion: {version}\nDownload size: {size}\nDigest: {digest}": "Prístup k obrazu bol potvrdený.\n\n{reference}\n\nArchitektúra: {architecture}\nVerzia: {version}\nVeľkosť na stiahnutie: {size}\nDigest: {digest}", "Image cache": "Comment", "Image compatibility restored:": "Kompatibilita s obrázkom obnovená:", "Image compatibility verified:": "Kompatibilita s obrázkom overená:", "Image directory:": "Priečinok s obrazmi:", "Image download failed:": "Sťahovanie obrázku zlyhalo:", "Image downloaded": "Stiahnutý obrázok", - "Image downloaded and verified.\n\n{reference}\n\nStorage: {storage}\nArchive size: {size}\nArchive: {archive}\n\nNo container was created.\nProxMenux removes unused cached images after successful updates.": "Obrázok bol stiahnutý a overený.\n\n{reference}\n\nSkladovanie: {storage}\nVeľkosť archívu: {size}\nArchív: {archive}\n\nNebol vytvorený žiadny kontajner.\nProxMenux po úspešných aktualizáciách odstráni nepoužívané obrázky z vyrovnávacej pamäte.", + "Image downloaded and verified.\n\n{reference}\n\nStorage: {storage}\nArchive size: {size}\nArchive: {archive}\n\nNo container was created.\nProxMenux removes unused cached images after successful updates.": "Obraz bol stiahnutý a overený.\n\n{reference}\n\nÚložisko: {storage}\nVeľkosť archívu: {size}\nArchív: {archive}\n\nNebol vytvorený žiadny kontajner.\nProxMenux po úspešných aktualizáciách odstraňuje nepoužívané obrazy z vyrovnávacej pamäte.", "Image file not found:": "Súbor obrazu sa nenašiel:", "Image imported:": "Obraz importovaný:", - "Image integrity verification failed. The partial download was discarded; try again.": "Overenie integrity obrázka zlyhalo. Čiastočné stiahnutie bolo zahodené; skúste to znova.", + "Image integrity verification failed. The partial download was discarded; try again.": "Overenie integrity obrazu zlyhalo. Neúplne stiahnuté dáta boli zahodené; skúste to znova.", "Image integrity verified": "Potvrdená integrita obrázku", "Image not allowed for the host monitor profile": "Obrázok nie je povolený pre profil monitora hostiteľa", - "Image reference": "Odkaz na obrázok", - "Image reference (for example ghcr.io/user/application:latest)": "Odkaz na image (napríklad ghcr.io/user/aplikácia:latest)", - "Image selection was cancelled": "Výber image bol zrušený", - "Image that is not in the catalog": "Image, ktorý nie je v katalógu", + "Image reference": "Odkaz na obraz", + "Image reference (for example ghcr.io/user/application:latest)": "Odkaz na obraz (napríklad ghcr.io/user/application:latest)", + "Image selection was cancelled": "Výber obrazu bol zrušený", + "Image that is not in the catalog": "Obraz, ktorý nie je v katalógu", "Image:": "Image:", "Images to import:": "Image na import:", "Immich CUDA requires NVIDIA driver 545 or later": "Immich CUDA requires NVIDIA ovládač 545 alebo neskôr", @@ -3079,7 +3079,7 @@ "Install Samba inside CT:": "Nainštalovať Sambu vo vnútri CT:", "Install ZFS auto-snapshot": "Nainštalovať ZFS auto-snapshot", "Install a version from the branch the kernel names.": "Nainštalujte verziu z vetvy s názvami jadra.", - "Install an image as a native LXC": "Nainštalujte obrázok ako natívny LXC", + "Install an image as a native LXC": "Nainštalovať obraz ako natívny LXC", "Install an image that is not in the catalog": "Inštalovať obraz, ktorý nie je v katalógu", "Install analysis tools": "Nainštalovať analytické nástroje", "Install and configure": "Nainštalovať a nastaviť", @@ -3099,7 +3099,7 @@ "Install server packages inside CT:": "Nainštalovať serverové balíky vo vnútri CT:", "Install terminal multiplexers": "Nainštalovať terminálové multiplexery", "Install the Edge TPU runtime (libedgetpu1-std)": "Nainštaluje Edge TPU runtime (libedgetpu1-std)", - "Install this image?": "Nainštalovať tento obrázok?", + "Install this image?": "Nainštalovať tento obraz?", "Install with Cloud-Init script": "Inštalovať cez Cloud-Init skript", "Install with ISO from UUP Dump": "Inštalovať z ISO cez UUP Dump", "Install with advanced settings": "Inštalovať s pokročilým nastavením", @@ -3296,9 +3296,9 @@ "Invalid host path:": "Neplatná cesta k hostiteľovi:", "Invalid host-monitor firewall plan": "Neplatný plán firewallu pre monitor hostiteľa", "Invalid image probe descriptor": "Neplatný deskriptor obrazovej sondy", - "Invalid image tag.": "Neplatná značka obrázka.", + "Invalid image tag.": "Neplatný tag obrazu.", "Invalid input": "Neplatný vstup", - "Invalid internal volume": "Neplatný vnútorný objem", + "Invalid internal volume": "Neplatné interné dátové úložisko", "Invalid list:": "Neplatný zoznam:", "Invalid machine learning CPU allocation:": "Neplatné pridelenie CPU pre strojové učenie:", "Invalid machine learning resources": "Neplatné zdroje strojového učenia", @@ -3384,7 +3384,7 @@ "It is strongly recommended to create a backup of your container before proceeding with the conversion.": "Pred prevodom sa dôrazne odporúča vytvoriť zálohu kontajnera.", "It needs a privileged container, which is not isolated from the host.": "Potrebuje privilegovaný kontajner, ktorý nie je izolovaný od hostiteľa.", "It will be installed from the official GitHub repository.": "Nainštaluje sa z oficiálneho GitHub repozitára.", - "It works through the Docker engine of the host, and a native OCI container does not have one.": "Funguje prostredníctvom Docker motora hostiteľa, a natívne OCI kontajner nemá jeden.", + "It works through the Docker engine of the host, and a native OCI container does not have one.": "Používa Docker engine hostiteľa, ktorý natívny OCI kontajner nemá k dispozícii.", "Italian": "Taliančina", "Its Compose file asks for privileged mode; the container is created unprivileged and that mode is only offered as an option.": "Jeho súbor Compose požaduje privilegovaný režim; kontajner je vytvorený neprivilegovaný a tento režim je ponúkaný len ako možnosť.", "Its ROCm profile is not offered.": "Jeho profil ROCm nie je ponúkaný.", @@ -3596,8 +3596,8 @@ "Log2RAM write scheduled every": "Zápis z Log2RAM je naplánovaný každých", "Log:": "Záznam:", "Logged-in users": "Prihlásení používatelia", - "Login to {registry} timed out. Saved credentials were not changed.": "Prihlásenie do {registry} vypršalo. Uložené poverenia sa nezmenili.", - "Logout from {registry} timed out. Saved file credentials were not changed.": "Odhlásenie z {registry} vypršalo. Prihlasovacie údaje uloženého súboru sa nezmenili.", + "Login to {registry} timed out. Saved credentials were not changed.": "Pri prihlasovaní do {registry} uplynul časový limit. Uložené prihlasovacie údaje sa nezmenili.", + "Logout from {registry} timed out. Saved file credentials were not changed.": "Pri odhlasovaní z {registry} uplynul časový limit. Prihlasovacie údaje uložené v súbore sa nezmenili.", "Logrotate optimization completed": "Optimalizácia logrotate je dokončená", "Logrotate service restarted successfully": "Služba logrotate bola úspešne reštartovaná", "Lollypop is a lightweight modern music player designed to work excellently on the GNOME desktop environment.": "Lollypop je ľahký moderný hudobný prehrávač navrhnutý tak, aby pracoval vynikajúco na GNOME desktop prostredí.", @@ -3660,7 +3660,7 @@ "Manage custom paths (add / remove your folders)": "Spravovať vlastné cesty (pridať / odstrániť priečinky)", "Manage installed OCI applications": "Spravovať nainštalované aplikácie OCI", "Manage local backup target": "Spravovať lokálny cieľ záloh", - "Manage private image registries.\nSaved credentials let ProxMenux access your private images.": "Správa súkromných registrov obrázkov.\nUložené poverenia umožňujú ProxMenux prístup k vašim súkromným obrázkom.", + "Manage private image registries.\nSaved credentials let ProxMenux access your private images.": "Správa súkromných registrov obrazov.\nUložené prihlasovacie údaje umožňujú ProxMenuxu pristupovať k vašim súkromným obrazom.", "Manage registry accounts as root on the Proxmox node.": "Spravujte účty registra ako root na uzle Proxmox.", "Managed disks must have backup enabled and a valid size": "Spravované disky musia mať zapnuté zálohovanie a platnú veľkosť", "Managing Nginx proxy hosts with a simple, powerful interface.": "Správa Nginx proxy hostiteľov s jednoduchým, výkonným rozhraním.", @@ -4085,7 +4085,7 @@ "No CTs available in the system.": "V systéme nie sú dostupné žiadne CT.", "No Changes Needed": "Nie sú potrebné žiadne zmeny", "No Cleanup Needed": "Čistenie nie je potrebné", - "No Compose file was given": "Nebol zadaný žiadny súbor", + "No Compose file was given": "Nebol zadaný žiadny súbor Compose", "No Controller/NVMe selected for now.": "Zatiaľ nie je vybraný žiadny Controller/NVMe.", "No Coral Detected": "Coral sa nenašiel", "No Coral TPU device was found on this host (neither PCIe/M.2 nor USB).": "Na tomto hoste sa nenašlo žiadne Coral TPU zariadenie (ani PCIe/M.2, ani USB).", @@ -4461,8 +4461,8 @@ "Online retro games emulator": "Online retro hry emulátor", "Only Intel/AMD DRM, Coral and USB nodes can be adopted automatically": "Automaticky je možné prijať iba uzly Intel/AMD DRM, Coral a USB", "Only Intel/AMD DRM, Coral, NPU and USB nodes can be adopted automatically": "Automaticky je možné prijať iba uzly Intel/AMD DRM, Coral, NPU a USB", - "Only a Docker Swarm uses these settings, so they are not applied:": "Tieto nastavenia používa len roj Docker, takže sa nepoužívajú:", - "Only a full sha256 image digest is supported.": "Podporovaný je iba úplný súhrn obrázkov sha256.", + "Only a Docker Swarm uses these settings, so they are not applied:": "Tieto nastavenia používa iba Docker Swarm, preto sa neuplatnia:", + "Only a full sha256 image digest is supported.": "Podporovaný je iba úplný digest obrazu sha256.", "Only an installed standalone OCI instance can adopt external changes": "Externé zmeny môže prijať iba nainštalovaná samostatná inštancia OCI", "Only backed-up Proxmox volumes can be adopted": "Je možné prijať iba zálohované zväzky Proxmox", "Only convert to privileged if absolutely necessary for your use case.": "Na privilegovaný kontajner prevádzajte iba vtedy, keď je to naozaj potrebné.", @@ -4622,7 +4622,7 @@ "Passthrough may still work without a ROM file.": "Passthrough môže fungovať aj bez ROM súboru.", "Passthrough may work, but startup/restart reliability is not guaranteed.": "Priame priradenie môže fungovať, ale spoľahlivý štart alebo reštart VM nie je zaručený.", "Password": "Heslo", - "Password / access token (image pull permission)": "Heslo / prístupový token (povolenie na stiahnutie obrázka)", + "Password / access token (image pull permission)": "Heslo / prístupový token (oprávnenie sťahovať obrazy)", "Password Mismatch": "Heslá sa nezhodujú", "Password cannot be empty.": "Heslo nesmie byť prázdne.", "Password confirmation cannot be empty.": "Potvrdenie hesla nesmie byť prázdne.", @@ -4671,7 +4671,7 @@ "Path must be absolute (start with /)": "Cesta musí byť absolútna (začínať znakom /)", "Path must be absolute (start with /).": "Cesta musí byť absolútna (začínať znakom /).", "Path not found": "Cesta sa nenašla", - "Path of the Compose file": "Cesta ku súboru", + "Path of the Compose file": "Cesta k súboru Compose", "Path of the oci-compose file": "Cesta k súboru oci-compose", "Path removed:": "Cesta bola odstránená:", "Path:": "Cesta:", @@ -4889,11 +4889,11 @@ "ProxMenux attaches directories, not single files, so this image cannot be installed yet.": "ProxMenux pripája adresáre, nie jednotlivé súbory, takže tento obrázok ešte nie je možné nainštalovať.", "ProxMenux can apply open permissions on this NFS directory from the host so the container can read and write:": "ProxMenux môže na hostovi nastaviť otvorené oprávnenia pre tento NFS priečinok, aby kontajner mohol čítať aj zapisovať:", "ProxMenux can remount it with open permissions so any LXC can read and write.": "ProxMenux ho môže znovu pripojiť s otvorenými oprávneniami, aby každý LXC mohol čítať aj zapisovať.", - "ProxMenux cannot install this image automatically. Import its Compose file instead.": "ProxMenux nemôže nainštalovať tento obrázok automaticky. Namiesto toho importujte jeho súbor Compose.", + "ProxMenux cannot install this image automatically. Import its Compose file instead.": "ProxMenux nedokáže automaticky nainštalovať tento obraz. Importujte jeho súbor Compose.", "ProxMenux cannot override NFS server-side permissions from the host.": "ProxMenux nevie z hosta prepísať oprávnenia nastavené na NFS serveri.", - "ProxMenux could not translate this image. Try importing its Compose file from OCI manager Apps.": "ProxMenux nemohol preložiť tento obrázok. Skúste importovať jeho súbor Compose z OCI manager Apps.", + "ProxMenux could not translate this image. Try importing its Compose file from OCI manager Apps.": "ProxMenux nedokázal previesť tento obraz na konfiguráciu LXC. Skúste importovať jeho súbor Compose cez správcu aplikácií OCI.", "ProxMenux customizations removed from bashrc": "Úpravy ProxMenux boli odstránené z bashrc", - "ProxMenux does not give a container the system of its host.": "ProxMenux nedáva kontajner systém svojho hostiteľa.", + "ProxMenux does not give a container the system of its host.": "ProxMenux nesprístupňuje kontajneru systémové adresáre hostiteľa.", "ProxMenux does not install them. The installation asks how to connect to them.": "ProxMenux ich neinštaluje. Inštalácia sa pýta, ako sa k nim pripojiť.", "ProxMenux does not validate the contents; any keyfile your PBS accepts is accepted here.": "ProxMenux nekontroluje obsah; prijme sa každý keyfile, ktorý prijme vaše PBS.", "ProxMenux files:": "Súbory ProxMenux:", @@ -4934,7 +4934,7 @@ "Proxmox repository configuration completed": "Nastavenie repozitárov Proxmoxu je dokončené", "Proxmox repository fixed (no-subscription, candidate is 9.x)": "Proxmox repozitár opravený (no-subscription, kandidát je 9.x)", "Proxmox status:": "Stav Proxmoxu:", - "Proxmox storage for the volume": "Uskladnenie Proxmox pre objem", + "Proxmox storage for the volume": "Úložisko Proxmox pre dátový disk", "Proxmox storages:": "Úložiská Proxmoxu:", "Proxmox system repair completed successfully!": "Oprava systému Proxmox bola úspešne dokončená.", "Proxmox system repair completed with some issues.": "Oprava systému Proxmox bola dokončená, ale s niektorými problémami.", @@ -5018,7 +5018,7 @@ "Reading SMART data...": "Čítam SMART dáta...", "Reading SMART self-test log...": "Čítam log SMART samo-testu...", "Reading full SMART report...": "Čítam úplnú SMART správu...", - "Reading image metadata. Please wait...": "Čítanie metadát obrázkov. Čakajte prosím...", + "Reading image metadata. Please wait...": "Načítavajú sa metadáta obrazu. Čakajte, prosím...", "Real-time Performance Monitoring": "Monitorovanie výkonnosti v reálnom čase", "Real-time bandwidth usage (press q to exit)": "Využitie priepustnosti v reálnom čase (ukončíte klávesom q)", "Real-time collaborative document editor": "Kolektívne editor dokumentov v reálnom čase", @@ -5113,9 +5113,9 @@ "Register mount path in Proxmox:": "Zaregistrovať cestu pripojenia v Proxmoxe:", "Registering disk as Proxmox storage...": "Registrujem disk ako úložisko Proxmoxu...", "Registering module with DKMS...": "Registrujem modul v DKMS...", - "Registry accounts": "Účty v registri", + "Registry accounts": "Účty registrov", "Registry hostname": "Názov hostiteľa registra", - "Registry: {registry}\nAccount: {account}\nAuthentication: {method}\n\nSaved credentials are verified when an image is accessed.": "Register: {registry}\nÚčet: {account}\nOverenie: {method}\n\nUložené poverenia sa overia pri prístupe k obrázku.", + "Registry: {registry}\nAccount: {account}\nAuthentication: {method}\n\nSaved credentials are verified when an image is accessed.": "Register: {registry}\nÚčet: {account}\nOverenie: {method}\n\nUložené prihlasovacie údaje sa overia pri prístupe k obrazu.", "Reinstall / update Coral drivers": "Preinštalovať / aktualizovať ovládače Coral", "Reinstall Fail2Ban": "Preinštalovať Fail2Ban", "Reinstall Lynis": "Preinštalovať Lynis", @@ -5187,12 +5187,12 @@ "Remove obsolete systemd-boot meta-package": "Odstrániť zastaraný meta-balík systemd-boot", "Remove old repository files:": "Odstráňte staré súbory repozitárov:", "Remove passthrough device from VM": "Odstrániť passthrough zariadenie z VM", - "Remove saved credentials": "Odstráňte uložené poverenia", + "Remove saved credentials": "Odstrániť uložené prihlasovacie údaje", "Remove subscription banner": "Odstrániť hlásenie o predplatnom", "Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Odstrániť aplikáciu? Jeho kontajnerové disky sa vymažú a iba záloha ich môže vrátiť späť.", "Remove the lock left on a VM by an interrupted task": "Odstrániť zámok, ktorý na VM zanechala prerušená úloha", "Remove the lock left on a container by an interrupted task": "Odstrániť zámok, ktorý na kontajneri zanechala prerušená úloha", - "Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Odstrániť uložené poverenia pre {registry}?\nSpustené aplikácie budú naďalej fungovať.", + "Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Odstrániť uložené prihlasovacie údaje pre {registry}?\nSpustené aplikácie budú naďalej fungovať.", "Remove the unprivileged flag from configuration:": "Odstrániť príznak neprivilegovaného kontajnera z nastavenia:", "Remove unused packages and their config": "Odstrániť nepoužívané balíky aj ich nastavenia", "Remove: delete the application and its containers": "Odstrániť: odstrániť aplikáciu a jej obaly", @@ -5519,7 +5519,7 @@ "Save and classify articles. Read them later. Freely.": "Uložiť a klasifikovať články. Prečítajte si ich neskôr. Slobodne.", "Save the passphrase somewhere safe NOW, before continuing.": "Uložte si frázu TERAZ na bezpečné miesto, ešte pred pokračovaním.", "Save this Borg target so you don't need to enter the details again?": "Uložiť tento Borg cieľ, aby ste údaje nemuseli zadávať znova?", - "Saved credentials for {registry} were removed.\nOther Skopeo/Docker credential files are managed separately.": "Uložené poverenia pre {registry} boli odstránené.\nOstatné súbory poverení Skopeo/Docker sa spravujú samostatne.", + "Saved credentials for {registry} were removed.\nOther Skopeo/Docker credential files are managed separately.": "Uložené prihlasovacie údaje pre {registry} boli odstránené.\nOstatné súbory s prihlasovacími údajmi Skopeo/Docker sa spravujú samostatne.", "Saved record removed": "Uložený záznam odstránený", "Saving changes...": "Ukladajú sa zmeny...", "Saving the new configuration": "Ukladanie novej konfigurácie", @@ -5903,11 +5903,11 @@ "Shutdown timeout": "Čas na vypnutie vypršal", "SiYuan access code": "Prístupový kód SiYuan", "SickGear provides management of TV shows and/or Anime, it detects new episodes, links downloader apps, and more..": "SickGear poskytuje správu televíznych relácií a / alebo Anime, detekuje nové epizódy, odkazy aplikácie pre downloader, a ďalšie..", - "Sign in to a registry": "Prihláste sa do registra", + "Sign in to a registry": "Prihlásiť sa do registra", "Sign in to the panel and create a Samba user in Settings before publishing a directory over SMB.": "Pred publikovaním adresára cez SMB sa prihláste na panel a vytvorte používateľa Samba v Nastaveniach.", "Signal is a messaging app with privacy at its core. It is free and easy to use, with strong end-to-end encryption that keeps your communication completely private.": "Signal je aplikácia správ s ochranou súkromia v jej jadre. Je zadarmo a ľahko použiteľný, so silným end-to-end šifrovaním, ktoré udržuje vašu komunikáciu úplne súkromnú.", "Signatures removed. Partition table preserved.": "Podpisy boli odstránené. Tabuľka oddielov zostala zachovaná.", - "Signed in to {registry} as {username}.\n\nCredentials are saved for ProxMenux.\nSelect the account to check, download or install an image.": "Prihlásený do {registry} ako {username}.\n\nPoverenia sú uložené pre ProxMenux.\nVyberte účet, ktorý chcete skontrolovať, stiahnuť alebo nainštalovať obrázok.", + "Signed in to {registry} as {username}.\n\nCredentials are saved for ProxMenux.\nSelect the account to check, download or install an image.": "Prihlásenie do {registry} ako {username} bolo úspešné.\n\nPrihlasovacie údaje sú uložené pre ProxMenux.\nVyberte účet, cez ktorý chcete overiť prístup k obrazu, stiahnuť ho alebo ho nainštalovať.", "Simple and easy to use DDNS": "Jednoduché a jednoduché použitie DDNS", "Single GPU Warning": "Upozornenie na jednu GPU", "Single target found — selected automatically:": "Našiel sa jeden target - vybraný automaticky:", @@ -5934,7 +5934,7 @@ "Skipping installation.": "Inštalácia sa preskakuje.", "Skipping manual patches — feranick fork already supports this kernel.": "Ručné záplaty preskakujem - fork feranick už tento kernel podporuje.", "Skopeo is not available. Restore the ProxMenux OCI dependencies and try again.": "Skopeo nie je k dispozícii. Obnovte závislosti ProxMenux OCI a skúste to znova.", - "Skopeo returned an invalid credential file; saved credentials were not changed.": "Skopeo vrátilo neplatný súbor poverení; uložené prihlasovacie údaje neboli zmenené.", + "Skopeo returned an invalid credential file; saved credentials were not changed.": "Skopeo vrátilo neplatný súbor s prihlasovacími údajmi; uložené prihlasovacie údaje sa nezmenili.", "Sleek podcast downloader with GPodder sync": "Sleek podcast downloader s GPodder synchronizácia", "Smart restore plan — hardware compatibility check": "Inteligentný plán obnovy - kontrola kompatibility hardvéru", "Smokeping keeps track of your network latency. For a full example of what this application is capable of visit UCDavis.": "Smokeping sleduje latenciu vašej siete. Úplný príklad toho, čo je táto aplikácia schopná navštíviť UCDavis.", @@ -6250,11 +6250,11 @@ "The Bookmark Everything App": "Záložka Všetko App", "The Brave browser is a fast, private and secure web browser for PC, Mac and mobile.": "Prehliadač Brave je rýchly, súkromný a bezpečný webový prehliadač pre PC, Mac a mobil.", "The CT already exists:": "CT už existuje:", - "The Compose file declares no service": "Zostaviť súbor deklaruje žiadnu službu", + "The Compose file declares no service": "Súbor Compose neobsahuje žiadnu službu", "The Compose file describes several images:": "Skladací súbor popisuje niekoľko obrázkov:", "The Compose file describes several services:": "Súbor Compose opisuje viacero služieb:", - "The Compose file does not contain a Compose document": "Skladací súbor neobsahuje dokument", - "The Compose file is not valid YAML:": "Skladací súbor nie je platný", + "The Compose file does not contain a Compose document": "Súbor neobsahuje dokument Compose", + "The Compose file is not valid YAML:": "Súbor Compose nie je platný YAML:", "The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. Continue only if you trust the image and accept this risk.": "Skladba ponúka voliteľnú AppArmor alebo seccomp relaxáciu; zostane vypnutá, ak si ju užívateľ nevyberie. Pokračujte len vtedy, ak dôverujete obrazu a prijmete toto riziko.", "The Compose value must be text or a list:": "Kompozitná hodnota musí byť text alebo zoznam:", "The DRM node is not an Intel or AMD GPU; NVIDIA requires its library profile": "DRM uzol nie je Intel ani AMD GPU; NVIDIA requires svoj profil knižnice", @@ -6377,10 +6377,10 @@ "The changes need the application installer": "Zmeny vyžadujú inštalačný program aplikácie", "The character device does not exist:": "Znakové zariadenie neexistuje:", "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Príkaz vyžaduje heslo, ktoré nie je ozvene. Po vytvorení užívateľov, webové rozhranie žiada o užívateľské meno a heslo.", - "The command does not name an image": "Príkaz nepomenuje obrázok", - "The command reads its variables from a file; write them in the command or use a Compose file": "Príkaz číta svoje premenné zo súboru; zapíšte ich do príkazu alebo použite súbor", - "The command runs the container as the user of the host; the container uses the user of its image instead.": "Príkaz spustí kontajner ako užívateľ hostiteľa; kontajner používa užívateľa svojho obrazu miesto.", - "The command uses options that cannot be translated:": "Príkaz používa možnosti, ktoré nemožno preložiť:", + "The command does not name an image": "Príkaz neuvádza žiadny obraz", + "The command reads its variables from a file; write them in the command or use a Compose file": "Príkaz číta premenné zo súboru; zadajte ich priamo v príkaze alebo použite súbor Compose", + "The command runs the container as the user of the host; the container uses the user of its image instead.": "Príkaz požaduje spustenie kontajnera pod používateľom hostiteľa; kontajner namiesto toho použije používateľa svojho obrazu.", + "The command uses options that cannot be translated:": "Príkaz používa voľby, ktoré nemožno previesť na nastavenia LXC:", "The command works out a value by running another command:": "Príkaz vytvorí hodnotu spustením iného príkazu:", "The compatibility check raised failures that may break the system after restore.": "Kontrola kompatibility našla zlyhania, ktoré môžu po obnove poškodiť systém.", "The configuration changed after the backup was restored; the recovery is not confirmed": "Konfigurácia zmenená po obnovení zálohy; obnova nie je potvrdená", @@ -6390,7 +6390,7 @@ "The configuration must run as root on Proxmox VE": "Konfigurácia musí bežať ako koreň na Proxmox VE", "The configuration of a new member changed after it was created": "Nastavenie nového člena sa zmenilo po jeho vytvorení", "The configuration stopped because of an unexpected error": "Konfigurácia sa zastavila kvôli neočakávanej chybe", - "The configured credential helper could not be used. Check its installation and credential store.": "Nepodarilo sa použiť nakonfigurovaného pomocníka poverení. Skontrolujte jeho inštaláciu a úložisko poverení.", + "The configured credential helper could not be used. Check its installation and credential store.": "Nakonfigurovaný pomocný program na správu prihlasovacích údajov sa nedá použiť. Skontrolujte jeho inštaláciu a úložisko prihlasovacích údajov.", "The console of the container could not be configured": "Konzolu kontajnera nebolo možné nakonfigurovať", "The consume/export volumes need at least 1 GB": "Objem spotreby/vývozu potrebuje najmenej 1 GB", "The container could not be removed automatically:": "Nádoba sa nedala automaticky odstrániť:", @@ -6403,7 +6403,7 @@ "The container does not exist:": "Nádoba neexistuje:", "The container does not have the fuse=1 feature enabled": "Kontajner nemá poistky = 1 zapnuté", "The container does not need it any more; an update downloads the new version when there is one.": "Kontajner už nepotrebuje; aktualizácia si stiahne novú verziu, keď je.", - "The container gets its own address and its own volumes, so the networks and volumes declared in the file are not used.": "Kontajner dostane svoju vlastnú adresu a svoje vlastné objemy, takže siete a objemy deklarované v súbore nie sú použité.", + "The container gets its own address and its own volumes, so the networks and volumes declared in the file are not used.": "Kontajner dostane vlastnú adresu a vlastné dátové úložiská, preto sa siete a úložiská definované v súbore nepoužijú.", "The container has advanced Proxmox settings outside the supported profile": "Kontajner má pokročilé nastavenia Proxmox mimo podporovaného profilu", "The container has an operation pending; finish or recover it first": "Kontajner má čakajúcu operáciu;najskôr ho dokončite alebo obnovte", "The container identity changed; nothing was adopted": "Identita kontajnera sa zmenila;nič nebolo prijaté", @@ -6423,13 +6423,13 @@ "The container stopped before the GPU permissions were verified:": "Kontajner sa zastavil pred overením povolení GPU:", "The container stopped before the application responded:": "Nádobka sa pred podaním žiadosti zastavila:", "The container stopped:": "Kontajner sa zastavil:", - "The container takes its time zone from Proxmox, so the time files of the host are not attached to it.": "Kontajner trvá časové pásmo od Proxmox, takže časové súbory hostiteľa nie sú pripojené k nemu.", + "The container takes its time zone from Proxmox, so the time files of the host are not attached to it.": "Kontajner preberá časové pásmo z Proxmoxu, preto sa doň nepripájajú súbory hostiteľa s nastavením času.", "The container was changed outside ProxMenux and an update would discard those changes:": "Kontajner bol zmenený mimo ProxMenux a aktualizácia by sa zbaviť týchto zmien:", "The container was created from a downloaded OCI image": "Kontajner bol vytvorený zo stiahnutého obrázku OCI", "The containers do not need them any more; an update downloads the new versions when there are any.": "Kontajnery ich už nepotrebujú; aktualizácia stiahne nové verzie, ak nejaké existujú.", "The containers were created from downloaded OCI images": "Kontajnery boli vytvorené zo stiahnutých obrázkov OCI", "The coordinated backup was modified": "Koordinovaná záloha bola zmenená", - "The credential file changed during this operation. Try again; it was not overwritten.": "Počas tejto operácie sa zmenil súbor poverení. Skúste to znova;nebolo to prepísané.", + "The credential file changed during this operation. Try again; it was not overwritten.": "Súbor s prihlasovacími údajmi sa počas tejto operácie zmenil. Skúste to znova; súbor nebol prepísaný.", "The current driver will be completely uninstalled before installing the new version. Continue?": "Aktuálny ovládač sa pred inštaláciou novej verzie úplne odinštaluje. Pokračovať?", "The current image of the saved channel will be checked and downloaded. Resources, paths and GPU are kept. The CT is stopped during the replacement and a native backup is created first.": "Aktuálny obraz uloženého kanála bude skontrolovaný a stiahnutý. Zdroje, cesty a GPU sú udržiavané. CT je zastavený počas výmeny a natívne zálohovanie je vytvorené ako prvé.", "The current record is missing for": "Aktuálny záznam chýba", @@ -6437,7 +6437,7 @@ "The current template changes the template identity or image repository; an explicit migration is required": "Aktuálna šablóna zmení identitu šablóny alebo úložisko obrázkov;vyžaduje sa explicitná migrácia", "The current template requires a new persistent path:": "Aktuálna šablóna requires novú pretrvávajúcu cestu:", "The custom path cannot hide system directories": "Vlastná cesta nemôže skryť systémové adresáre", - "The custom path overlaps another mount": "Vlastná cesta sa prekrýva s inou montážou", + "The custom path overlaps another mount": "Vlastná cesta sa prekrýva s iným bodom pripojenia", "The data and document volumes need at least 8 GB": "Údaje a objemy dokumentov potrebujú najmenej 8 GB", "The database server must accept connections from the IP address of this container, with a user that is not limited to localhost.": "Databázový server musí prijímať pripojenia z IP adresy tohto kontajnera s užívateľom, ktorý nie je obmedzený na localhost.", "The dedicated adapter still requires replaying its rootfs changes": "Špecializovaný adaptér stále requires prehráva svoje rootfs zmeny", @@ -6469,7 +6469,7 @@ "The file does not exist, is empty or is not readable.": "Súbor neexistuje, je prázdny alebo sa nedá čítať.", "The file does not exist:": "Súbor neexistuje:", "The file is not a ProxMenux oci-compose.": "Súbor nie je ProxMenux oci-compose.", - "The file is too large to be a Compose file": "Súbor je príliš veľký na to, aby bol súbor skladať", + "The file is too large to be a Compose file": "Súbor je príliš veľký na súbor Compose", "The file is too large to be an oci-compose": "Súbor je príliš veľký na to, aby bol oci-compose", "The filesystem": "Súborový systém", "The final cleanup did not complete:": "Konečné vyčistenie nedokončilo:", @@ -6514,10 +6514,10 @@ "The host-monitor firewall subnet changed; no firewall rule was added": "Podsieť firewallu pre monitor hostiteľa sa zmenila; žiadne pravidlo sa nepridalo", "The host-monitor web interface uses the host network.": "Webové rozhranie monitora hostiteľa používa sieť hostiteľa.", "The identity of a member was replaced": "Identita člena bola nahradená", - "The image cache path is unsafe. Check the template storage before trying again.": "Cesta vyrovnávacej pamäte obrázkov nie je bezpečná. Pred opätovným pokusom skontrolujte úložisko šablóny.", + "The image cache path is unsafe. Check the template storage before trying again.": "Cesta k vyrovnávacej pamäti obrazov nie je bezpečná. Pred ďalším pokusom skontrolujte úložisko šablón.", "The image changes the user expected by the adapter": "Obrázok mení používateľa očakávaný adaptérom", - "The image could not be downloaded or verified. Check registry and storage access, then try again.": "Obrázok sa nepodarilo stiahnuť alebo overiť. Skontrolujte prístup k registrom a ukladaciemu priestoru a skúste to znova.", - "The image could not be read from its registry:": "Obrázok sa nepodarilo prečítať z jeho registra:", + "The image could not be downloaded or verified. Check registry and storage access, then try again.": "Obraz sa nepodarilo stiahnuť alebo overiť. Skontrolujte prístup k registru a úložisku a skúste to znova.", + "The image could not be read from its registry:": "Obraz sa nepodarilo načítať z jeho registra:", "The image declares data paths that are still stored in the rootfs": "Obrázok deklaruje dátové cesty, ktoré sú stále uložené v rootfs", "The image did not grant the application user access to the devices; check its native init. Host permissions were not relaxed.": "Obrázok neumožňoval používateľovi aplikácie prístup k zariadeniam; skontrolujte jeho rodený init. Povolenie hostiteľa nebolo uvoľnené.", "The image did not pass the integrity check": "Obrázok neprešiel kontrolou integrity", @@ -6527,10 +6527,10 @@ "The image expects files that are given to it one by one:": "Obrázok očakáva súbory, ktoré sú mu dané jeden po druhom:", "The image is already up to date; nothing was changed.": "Obraz je už aktuálny, nič sa nezmenilo.", "The image is in its registry, for one architecture.": "Obraz je vo svojom registri, pre jednu architektúru.", - "The image is in its registry.": "Obrázok je v jeho registri.", - "The image is in its registry:": "Obrázok je vo svojom registri:", - "The image manifest failed integrity verification. Try again or contact the image publisher.": "Overenie integrity manifestu obrázka zlyhalo. Skúste to znova alebo kontaktujte vydavateľa obrázkov.", - "The image or tag was not found on {registry}, or this account cannot access it. Check the reference.": "Obrázok alebo značka sa na {registry} nenašla alebo k nim tento účet nemá prístup. Skontrolujte referenciu.", + "The image is in its registry.": "Obraz je dostupný vo svojom registri.", + "The image is in its registry:": "Obraz je dostupný vo svojom registri:", + "The image manifest failed integrity verification. Try again or contact the image publisher.": "Overenie integrity manifestu obrazu zlyhalo. Skúste to znova alebo kontaktujte vydavateľa obrazu.", + "The image or tag was not found on {registry}, or this account cannot access it. Check the reference.": "Obraz alebo tag sa v registri {registry} nenašiel alebo k nemu tento účet nemá prístup. Skontrolujte odkaz na obraz.", "The image requests NVIDIA, but the host has no working NVIDIA driver": "Obrázok požaduje NVIDIA, ale hostiteľ nemá žiadny funkčný ovládač NVIDIA", "The image requests disabling part of the AppArmor or seccomp confinement. Continue only if you trust the image and accept this risk.": "Obraz si vyžaduje vypnutie časti AppArmor alebo seccomp väzenia. Pokračujte len vtedy, ak dôverujete obrazu a prijmete toto riziko.", "The image requests disabling part of the AppArmor or seccomp confinement. The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. Continue only if you trust the image and accept this risk.": "Obraz si vyžaduje vypnutie časti AppArmor alebo seccomp väzenia. Skladba ponúka voliteľnú AppArmor alebo seccomp relaxáciu; zostane vypnutá, ak si ju užívateľ nevyberie. Pokračujte len vtedy, ak dôverujete obrazu a prijmete toto riziko.", @@ -6668,15 +6668,15 @@ "The record or diagnosis could not be completed; no update was run.": "Záznam alebo diagnóza nemohla byť dokončená; žiadna aktualizácia nebola spustená.", "The recovery did not complete. Review the log and choose \"Recover\" again for this container in the OCI management menu.": "Obnova sa nedokončila. Skontrolujte záznam a v ponuke Správca OCI aplikácií pri tomto kontajneri znova vyberte \"Obnoviť\".", "The recovery did not complete. Review the log and choose \"{choice}\" again for this container in the OCI management menu.": "Obnova sa nedokončila. Skontrolujte záznam a v ponuke Správca OCI aplikácií pri tomto kontajneri znova vyberte \"{choice}\".", - "The registry authentication file is invalid; it was not changed.": "Overovací súbor registra je neplatný;nebolo to zmenené.", - "The registry authentication file must be owned by root with mode 600.": "Overovací súbor registra musí vlastniť root s režimom 600.", - "The registry authentication file must have an absolute path.": "Overovací súbor registra musí mať absolútnu cestu.", - "The registry authentication path cannot contain symbolic links.": "Cesta na overenie registra nemôže obsahovať symbolické odkazy.", - "The registry authentication path has an unsafe parent directory.": "Cesta na overenie registra obsahuje nebezpečný nadradený adresár.", - "The registry credential directory must be owned by root with mode 700.": "Adresár poverení registra musí vlastniť root s režimom 700.", + "The registry authentication file is invalid; it was not changed.": "Súbor s prihlasovacími údajmi registrov je neplatný; nebol zmenený.", + "The registry authentication file must be owned by root with mode 600.": "Súbor s prihlasovacími údajmi registrov musí vlastniť root a musí mať oprávnenia 600.", + "The registry authentication file must have an absolute path.": "Cesta k súboru s prihlasovacími údajmi registrov musí byť absolútna.", + "The registry authentication path cannot contain symbolic links.": "Cesta k súboru s prihlasovacími údajmi registrov nesmie obsahovať symbolické odkazy.", + "The registry authentication path has an unsafe parent directory.": "Nadradený adresár súboru s prihlasovacími údajmi registrov nie je bezpečný.", + "The registry credential directory must be owned by root with mode 700.": "Adresár s prihlasovacími údajmi registrov musí vlastniť root a musí mať oprávnenia 700.", "The registry did not return the saved image digest": "Register nevrátil uložený image digest", - "The registry returned incomplete or unsupported image metadata.": "Register vrátil neúplné alebo nepodporované metadáta obrázka.", - "The registry returned invalid or unsupported image metadata.": "Register vrátil neplatné alebo nepodporované metadáta obrázka.", + "The registry returned incomplete or unsupported image metadata.": "Register vrátil neúplné alebo nepodporované metadáta obrazu.", + "The registry returned invalid or unsupported image metadata.": "Register vrátil neplatné alebo nepodporované metadáta obrazu.", "The remote does not exist; create and authorize it first in the WebUI:": "Vzdialenosť neexistuje; vytvoriť a schváliť ju ako prvý na WebUI:", "The remote installer must run as root on Proxmox VE": "Diaľkový inštalátor musí bežať ako koreň na Proxmox VE", "The remote must already be created and authorized in the Rclone web UI. This operation restarts the CT and publishes two FUSE views on the host.": "Vzdialený musí byť už vytvorený a schválený v Rclone web UI. Tento operation reštartuje CT a zverejňuje dva pohľady FUSE na hostiteľa.", @@ -6753,8 +6753,8 @@ "The server may require authentication for actual share access.": "Server môže vyžadovať prihlásenie pre samotný prístup k zdieľaniu.", "The server refused password authentication for": "Server odmietol prihlásenie heslom pre", "The server rejected": "Server odmietol", - "The service builds its own image; only a published image can be installed": "Služba buduje svoj vlastný obraz; môže byť nainštalovaný iba zverejnený obrázok", - "The service declares no image:": "Služba neprehlasuje žiadny obrázok:", + "The service builds its own image; only a published image can be installed": "Služba zostavuje vlastný obraz; nainštalovať možno iba zverejnený obraz", + "The service declares no image:": "Služba nemá zadaný žiadny obraz:", "The setting has no final value:": "Nastavenie nemá žiadnu konečnú hodnotu:", "The settings could not be fully rolled back. Review the container and its saved modification before continuing.": "Nepodarilo sa vrátiť všetky nastavenia. Pred pokračovaním skontrolujte kontajner a uloženú zmenu.", "The share already exists in smb.conf:": "Zdieľanie už existuje v smb.conf:", @@ -6844,7 +6844,7 @@ "This NFS share is fully restricted — even the host root cannot write to it.": "Toto NFS zdieľanie je úplne obmedzené - ani root na hostovi doň nevie zapisovať.", "This Proxmox Backup Server has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PBS source will be disabled.": "Tento Proxmox Backup Server nemá aktívne predplatné. Prepnúť na repozitár bez predplatného? Nedostupný zdroj Enterprise PBS bude vypnutý.", "This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Táto VM vyžaduje ďalšie kroky inštalácie. Návod nájdete tu:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144", - "This account is for {registry}. Enter an image from that registry.": "Tento účet je pre {registry}. Zadajte obrázok z tohto registra.", + "This account is for {registry}. Enter an image from that registry.": "Tento účet patrí registru {registry}. Zadajte obraz z tohto registra.", "This action will:": "Táto akcia vykoná:", "This application cannot be recovered yet:": "Túto aplikáciu zatiaľ nie je možné obnoviť:", "This application cannot be recovered yet; nothing was changed:": "Túto aplikáciu zatiaľ nie je možné obnoviť; nič sa nezmenilo:", @@ -6876,10 +6876,10 @@ "This explicitly marks the container as privileged": "Týmto sa kontajner výslovne označí ako privilegovaný", "This file decides what is mounted and with which privileges. Install it only if it comes from a source you trust.": "Tento súbor rozhoduje o tom, čo sa pripojí a s akými oprávneniami. Nainštalujte ho iba vtedy, ak pochádza zo zdroja, ktorému dôverujete.", "This guarantees that device nodes are available before applying LXC GPU config.": "Tým sa zabezpečí, že device nodes budú dostupné ešte pred použitím GPU nastavenia pre LXC.", - "This host architecture is not supported by the OCI image importer.": "Táto hostiteľská architektúra nie je podporovaná importérom obrázkov OCI.", + "This host architecture is not supported by the OCI image importer.": "Importér obrazov OCI nepodporuje architektúru tohto hostiteľa.", "This host has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PVE source will be disabled. Enterprise Ceph sources, if present, will be disabled without choosing a replacement Ceph channel; configure Ceph separately if needed.": "Tento hostiteľ nemá žiadne aktívne predplatné, prepnúť na úložisko bez predplatného? Neprístupný zdroj Enterprise PVE bude zakázaný. Enterprise Ceph zdroje, ak sú prítomné, budú deaktivované bez výberu náhradného Ceph kanála; v prípade potreby nakonfigurujte Ceph samostatne.", - "This image cannot be installed as it is described:": "Tento obrázok nie je možné nainštalovať, ako je popísané:", - "This image has no compatible Linux image for this host ({architecture}).": "Tento obrázok nemá žiadny kompatibilný obraz Linuxu pre tohto hostiteľa ({architecture}).", + "This image cannot be installed as it is described:": "Tento obraz nemožno nainštalovať podľa zadanej konfigurácie:", + "This image has no compatible Linux image for this host ({architecture}).": "Tento obraz nemá variant Linuxu kompatibilný s týmto hostiteľom ({architecture}).", "This image requires the host module": "Tento obrázok requires hostiteľský modul", "This import rejects these paths without checking whether they are files or directories.": "Tento import odmieta tieto cesty bez kontroly, či ide o súbory alebo adresáre.", "This installation will:": "Táto inštalácia urobí toto:", @@ -7145,7 +7145,7 @@ "Unsafe private configuration path": "Nebezpečná konfiguračná dráha", "Unsafe qBittorrent configuration path": "Nebezpečná konfiguračná dráha qBittorrent", "Unsafe record": "Nebezpečný záznam", - "Unsafe registry credential lock.": "Nebezpečný zámok poverení registra.", + "Unsafe registry credential lock.": "Zámok prihlasovacích údajov registrov nie je bezpečný.", "Unsafe registry directory": "Nebezpečný adresár registrov", "Unsafe registry lock": "Nebezpečný zámok registra", "Unsafe rootfs for the capture": "Nebezpečné rootfs pre zachytenie", @@ -7213,7 +7213,7 @@ "Update cancelled by user": "Aktualizácia zrušená používateľom", "Update completed. Data kept.": "Aktualizácia dokončená. Údaje uchovávané.", "Update completed. Press Enter to continue...": "Aktualizácia dokončená. Pokračujte stlačením Enter...", - "Update credentials": "Aktualizujte poverenia", + "Update credentials": "Aktualizovať prihlasovacie údaje", "Update every container of the application": "Aktualizovať každý kontajner aplikácie", "Update kernel to compatible version": "Aktualizovať kernel na kompatibilnú verziu", "Update now?": "Aktualizácia?", @@ -7286,7 +7286,7 @@ "Use a registry hostname, for example ghcr.io or registry.example.com:5000.": "Použite názov hostiteľa registra, napríklad ghcr.io alebo registry.example.com:5000.", "Use an existing SSH private key file on this host": "Použiť existujúci súbor súkromného SSH kľúča na tomto hostovi", "Use an existing keyfile": "Použiť existujúci keyfile", - "Use an image reference, for example ghcr.io/owner/app:latest.": "Použite odkaz na obrázok, napríklad ghcr.io/owner/app:latest.", + "Use an image reference, for example ghcr.io/owner/app:latest.": "Použite odkaz na obraz, napríklad ghcr.io/owner/app:latest.", "Use as-is — keep data and filesystem": "Použiť tak, ako je - ponechať dáta aj súborový systém", "Use content types according to your use case.": "Typy obsahu zvoľte podľa toho, na čo bude úložisko slúžiť.", "Use default": "Použiť predvolené", @@ -7477,7 +7477,7 @@ "Vivaldi is a Norwegian freeware, cross-platform web browser with a built-in email client developed by Vivaldi Technologies.": "Vivaldi je nórsky freeware, cross-platform webový prehliadač s vstavaným e-mailovým klientom vyvinutým Vivaldi Technologies.", "Volume configuration cancelled": "Konfigurácia hlasitosti zrušená", "Volume options are not yet supported": "Možnosti objemu zatiaľ nie sú podporované", - "Volume size in GB": "Veľkosť objemu v GB", + "Volume size in GB": "Veľkosť dátového disku v GB", "Volumes attached": "Pripojené objemy", "Volumes prepared for the first start:": "Objemy pripravené na prvý začiatok:", "Volumes shared between services are not yet supported": "Objemy zdieľané medzi službami zatiaľ nie sú podporované", @@ -7544,7 +7544,7 @@ "Webtop - Alpine, Ubuntu, Fedora, and Arch based containers containing full desktop environments in officially supported flavors accessible via any modern web browser.": "Webtop - Alpine, Ubuntu, Fedora, a Arch založené kontajnery obsahujúce plné pracovné prostredie v oficiálne podporovaných príchutiach prístupných cez akýkoľvek moderný webový prehliadač.", "Weekly, Sunday 02:00": "Týždenne, nedeľa 02:00", "Weixin (WeChat) is an instant messaging, social media, and mobile payment app developed by Tencent.": "Weixin (WeChat) je okamžité správy, sociálne médiá, a mobilné platobné aplikácie vyvinuté Tencent.", - "What cannot be translated:": "Čo sa nedá preložiť:", + "What cannot be translated:": "Čo nemožno previesť na nastavenia LXC:", "What do you want to change?": "Čo chcete zmeniť?", "What do you want to do?": "Čo chcete urobiť?", "What runs the recognition of Immich": "Čo spúšťa rozpoznávanie Immich", @@ -7687,7 +7687,7 @@ "Zeroing partition": "Nulujem oddiel", "Znc is an IRC network bouncer or BNC. It can detach the client from the actual IRC server, and also from selected channels. Multiple clients from different locations can connect to a single ZNC account simultaneously and therefore appear under the same nickname on IRC.": "Znc je vyhadzovač siete IRC alebo BNC. Môže odpojiť klienta od aktuálneho IRC servera, ako aj od vybraných kanálov. Viacerí klienti z rôznych miest sa môžu pripojiť na jeden ZNC účet súčasne, a preto sa objaví pod rovnakou prezývkou na IRC.", "Zotero is a free, easy-to-use tool to help you collect, organize, annotate, cite, and share research.": "Zotero je bezplatný, ľahko použiteľný nástroj, ktorý vám pomôže zhromažďovať, organizovať, anotovať, citovať a zdieľať výskum.", - "a device it asks for cannot be translated:": "zariadenie, ktoré požaduje, nemožno preložiť:", + "a device it asks for cannot be translated:": "požadované zariadenie nemožno pripojiť do LXC:", "a different host monitor include already exists:": "už existuje iný include hostiteľského monitora:", "a value is required": "hodnota je required", "aMule WebUI (password only, no username)": "aMule WebUI (iba heslo, žiadne užívateľské meno)", @@ -7772,12 +7772,12 @@ "disks present": "disky prítomné", "dkms autoinstall did not activate:": "dkms autoinstall sa neaktivoval:", "dkms.conf generated.": "dkms.conf bol vytvorený.", - "docker run command": "príkaz docker run", + "docker run command": "príkazu docker run", "docker run command of the application": "príkaz docker run aplikácie", "does not exist on this host. Path not added.": "na tomto hostovi neexistuje. Cesta nebola pridaná.", "does not exist. Exiting.": "neexistuje. Končím.", "doplarr_rs starts from the example configuration and connects to nothing. Write the token of your Discord bot in discord_token in /config/config.toml inside the container.": "doplarr_rs začína od konfigurácie príkladu a spája sa s ničím. Napíšte token svojho Discord bot v discord token v /config/config.toml vnútri kontajnera.", - "downloaded Compose file": "stiahnutý súbor", + "downloaded Compose file": "stiahnutého súboru Compose", "dpkg still reports unfinished package work; review": "dpkg stále hlási nedokončenú prácu s balíkom;recenzia", "driver components": "komponenty vodiča", "driver:": "ovládač:", @@ -7836,7 +7836,7 @@ "hostpci entries for all IOMMU group devices": "hostpci položky pre všetky zariadenia IOMMU skupiny", "hostpci entries for selected GPU functions (full IOMMU group will be enforced after reboot)": "hostpci položky pre vybrané funkcie GPU (celá IOMMU skupina sa vynúti po reštarte)", "hour(s)": "hod.", - "https if the image serves TLS": "https, ak obrázok slúži TLS", + "https if the image serves TLS": "https, ak obraz poskytuje TLS", "iSCSI Content Type": "Typ obsahu iSCSI", "iSCSI Daemon (iscsid): RUNNING": "iSCSI služba (iscsid): BEŽÍ", "iSCSI Daemon (iscsid): STOPPED": "iSCSI služba (iscsid): ZASTAVENÁ", @@ -7854,7 +7854,7 @@ "iSCSI tools installed": "iSCSI nástroje sú nainštalované", "iftop usage": "Použitie iftop", "image cache on": "Comment", - "image itself": "samotný obrázok", + "image itself": "samotného obrazu", "imported:": "importované:", "in CT": "v CT", "in backups": "v zálohách", @@ -7999,7 +7999,7 @@ "oldest driver offered:": "najstarší ponúkaný ovládač:", "on": "o", "on SSD/NVMe pools that support discard": "na SSD/NVMe pooloch, ktoré podporujú discard", - "one of its services declares no image": "jedna zo svojich služieb neprehlasuje žiadny obraz", + "one of its services declares no image": "jedna zo služieb nemá zadaný žiadny obraz", "one of its services is not written as a service": "jedna z jej služieb nie je napísaná ako služba", "openssl encryption failed.": "Šifrovanie cez openssl zlyhalo.", "openssl is not installed — cannot create recovery copy. Install openssl and retry.": "openssl nie je nainštalovaný - nedá sa vytvoriť obnovovacia kópia. Nainštalujte openssl a skúste to znova.", @@ -8014,7 +8014,7 @@ "packages.": "balíkov.", "parent PF:": "rodičovská PF:", "partition(s). Partition table preserved.": "oddielov. Tabuľka oddielov zostala zachovaná.", - "pasted Compose file": "vložený súbor", + "pasted Compose file": "vloženého súboru Compose", "paths for next boot (/etc/pve, guests, drivers, ...)": "cesty na najbližší štart (/etc/pve, hostia, ovládače, ...)", "pct exec authorization failed": "Autorizácia cez pct exec zlyhala", "pct push failed. Check log:": "pct push zlyhalo. Skontrolujte záznam:", @@ -8208,10 +8208,10 @@ "{path} already exists and belongs to another user of the host, so {app} cannot write to it.\n\nProxMenux can add an access rule (ACL) for the identity {app} uses on the host (UID {uid}). Nothing that is there changes owner or permissions: whatever reads or writes those files today keeps doing so, and the owner of the directory also gets access to the files {app} creates.\n\nGrant {app} access to this directory?": "{path} už existuje a patrí inému používateľovi hostiteľa, takže {app} naň nemôže zapisovať.\n\nProxMenux môže pridať pravidlo prístupu (ACL) pre identitu, ktorú {app} používa na hostiteľovi (UID {uid}).Nič z toho, čo tam je, nemení vlastníka ani oprávnenia: čokoľvek, čo dnes číta alebo zapisuje tieto súbory, to robí aj naďalej a vlastník adresára tiež získa prístup k súborom, ktoré {app} vytvorí.\n\nUdeliť {app} prístup k tomuto adresáru?", "{registry} answered with plain HTTP. Use a registry with HTTPS; TLS verification remains enabled.": "{registry} odpovedal jednoduchým HTTP. Použite register s HTTPS; Overenie TLS zostáva povolené.", "{registry} did not respond within {timeout} seconds. Try again later.": "{registry} neodpovedal do {timeout} sekúnd. Skúste to znova neskôr.", - "{registry} is rate limiting requests. Wait a while and try again.": "{registry} sú požiadavky obmedzujúce rýchlosť. Chvíľu počkajte a skúste to znova.", - "{registry} refused access to this image:\n{reference}\n\nIf the image is private, sign in with an account that can pull it.\n\nSign in now?": "{registry} odmietol prístup k tomuto obrázku:\n{reference}\n\nAk je obrázok súkromný, prihláste sa pomocou účtu, ktorý ho môže stiahnuť.\n\nPrihláste sa teraz?", + "{registry} is rate limiting requests. Wait a while and try again.": "Register {registry} obmedzuje počet požiadaviek. Chvíľu počkajte a skúste to znova.", + "{registry} refused access to this image:\n{reference}\n\nIf the image is private, sign in with an account that can pull it.\n\nSign in now?": "Register {registry} odmietol prístup k tomuto obrazu:\n{reference}\n\nAk je obraz súkromný, prihláste sa účtom, ktorý má oprávnenie stiahnuť ho.\n\nPrihlásiť sa teraz?", "{registry} reported a server error. Try again later.": "{registry} ohlásil chybu servera. Skúste to znova neskôr.", - "{registry} returned invalid or unsupported image metadata.": "{registry} vrátilo neplatné alebo nepodporované metadáta obrázka.", + "{registry} returned invalid or unsupported image metadata.": "Register {registry} vrátil neplatné alebo nepodporované metadáta obrazu.", "— Full/Custom restore will skip kernel-tied paths": "- úplná/vlastná obnova preskočí cesty viazané na kernel", "— disk may be busy. Skipping fstab removal.": "- disk sa možno používa. Preskakujem odstránenie z fstab.", "— restore proceeds as same-version (verified safe)": "- obnova pokračuje ako pri rovnakej verzii (overené ako bezpečné)", @@ -8243,5 +8243,25 @@ "⚠ Disk data will NOT be erased.": "⚠ Dáta na disku sa NEVYMAŽÚ.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disk sa odpojí a odstráni z /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ Záznam v /etc/fstab bude odstránený.", - "⚠ The disk will be unmounted.": "⚠ Disk bude odpojený." + "⚠ The disk will be unmounted.": "⚠ Disk bude odpojený.", + "it describes more than one image": "definuje viac než jeden obraz", + "it uses Compose configs, which have no equivalent here": "používa nastavenia Compose configs, ktoré tu nemajú ekvivalent", + "the devices it asks for are not written in a way that can be read": "požadované zariadenia sú zadané v nepodporovanom formáte", + "its health check is not written in a way that can be read": "kontrola stavu je zadaná v nepodporovanom formáte", + "its stop timeout is not written in a way that can be read": "časový limit zastavenia je zadaný v nepodporovanom formáte", + "its shared memory size is not written in a way that can be read": "veľkosť zdieľanej pamäte je zadaná v nepodporovanom formáte", + "the resource limits it asks for cannot be applied": "požadované limity systémových prostriedkov nemožno uplatniť", + "the memory limit it asks for cannot be applied": "požadovaný limit pamäte nemožno uplatniť", + "it asks for two different memory limits": "požaduje dva rozdielne limity pamäte", + "Compose environment must be a list or a mapping.": "Položka environment v súbore Compose musí byť zoznam alebo mapa kľúčov a hodnôt.", + "Compose volumes must be a list.": "Položka volumes v súbore Compose musí byť zoznam.", + "Compose ports must be a list.": "Položka ports v súbore Compose musí byť zoznam.", + "The Compose file has no services.": "Súbor Compose neobsahuje žiadne služby.", + "The Compose document must be a mapping.": "Dokument Compose musí byť mapa kľúčov a hodnôt.", + "The Compose file has no x-casaos metadata.": "Súbor Compose neobsahuje metadáta x-casaos.", + "x-casaos.main must name an existing service.": "Položka x-casaos.main musí odkazovať na existujúcu službu.", + "The main Compose service has no image.": "Hlavná služba v súbore Compose nemá zadaný žiadny obraz.", + "Invalid Compose volume path:": "Neplatná cesta dátového úložiska v súbore Compose:", + "Invalid Compose port:": "Neplatný port v súbore Compose:", + "Invalid application identifier:": "Neplatný identifikátor aplikácie:" } diff --git a/oci/src/proxmenux_oci/custom.py b/oci/src/proxmenux_oci/custom.py index 62f3acfa..acc14355 100644 --- a/oci/src/proxmenux_oci/custom.py +++ b/oci/src/proxmenux_oci/custom.py @@ -21,7 +21,7 @@ from .cli import install_template from .i18n import source_text from .installer import ADVANCED_MODE, DEFAULT_MODE from .converter import ConversionError -from .i18n import translate +from .i18n import N_, translate from .registry_auth import RegistryError, refuses_access, validate_image from .ui import UserCancelled @@ -533,16 +533,16 @@ def read_definition(ui) -> tuple[str, str]: BLOCKER_TEXTS = { - 'multi-service-compose': 'it describes more than one image', - 'native-multi-lxc-orchestrator-not-yet-implemented': 'it describes more than one image', - 'top-level-configs': 'it uses Compose configs, which have no equivalent here', - 'devices-format': 'the devices it asks for are not written in a way that can be read', - 'healthcheck-format': 'its health check is not written in a way that can be read', - 'stop-grace-period-format': 'its stop timeout is not written in a way that can be read', - 'shm-size-format': 'its shared memory size is not written in a way that can be read', - 'ulimits-format-or-resource': 'the resource limits it asks for cannot be applied', - 'mem-limit-format-or-below-proxmox-minimum': 'the memory limit it asks for cannot be applied', - 'mem-limit-deploy-consistency-review': 'it asks for two different memory limits', + 'multi-service-compose': N_('it describes more than one image'), + 'native-multi-lxc-orchestrator-not-yet-implemented': N_('it describes more than one image'), + 'top-level-configs': N_('it uses Compose configs, which have no equivalent here'), + 'devices-format': N_('the devices it asks for are not written in a way that can be read'), + 'healthcheck-format': N_('its health check is not written in a way that can be read'), + 'stop-grace-period-format': N_('its stop timeout is not written in a way that can be read'), + 'shm-size-format': N_('its shared memory size is not written in a way that can be read'), + 'ulimits-format-or-resource': N_('the resource limits it asks for cannot be applied'), + 'mem-limit-format-or-below-proxmox-minimum': N_('the memory limit it asks for cannot be applied'), + 'mem-limit-deploy-consistency-review': N_('it asks for two different memory limits'), } @@ -730,7 +730,7 @@ def _name(ui, template: dict[str, Any]) -> str: return name -def explore(ui) -> None: +def _explore(ui) -> None: """Reads a Compose file, a docker run command, an image reference or an oci-compose, reports what ProxMenux would install from it and installs it.""" text, origin = read_definition(ui) @@ -784,3 +784,43 @@ def explore(ui) -> None: else: name = _name(ui, template) install_template(ui, template, name, mode) + + +def conversion_message(error: ConversionError) -> str: + """Render legacy converter diagnostics at the custom-image UI boundary. + + Converter exceptions are also consumed by catalog tools. Keep their + original text, and any diagnostic data, outside this localized UI. + """ + text = str(error) + exact = { + 'environment must be a list or a mapping': N_('Compose environment must be a list or a mapping.'), + 'volumes must be a list': N_('Compose volumes must be a list.'), + 'ports must be a list': N_('Compose ports must be a list.'), + 'The CasaOS Compose file has no services': N_('The Compose file has no services.'), + 'The CasaOS document is not a Compose mapping': N_('The Compose document must be a mapping.'), + 'The Compose file has no x-casaos metadata': N_('The Compose file has no x-casaos metadata.'), + 'x-casaos.main no identifica un servicio valido': N_('x-casaos.main must name an existing service.'), + 'The main CasaOS service declares no image': N_('The main Compose service has no image.'), + } + prefixes = { + 'Ruta de volumen no valida:': N_('Invalid Compose volume path:'), + 'Puerto no valido:': N_('Invalid Compose port:'), + 'Cannot normalise the CasaOS identifier:': N_('Invalid application identifier:'), + 'Docker Compose CasaOS no valido:': N_('The Compose file is not valid YAML:'), + } + if text in exact: + translated = translate(exact[text]) + return text if translated == exact[text] else translated + for prefix, source in prefixes.items(): + if text.startswith(prefix): + translated = translate(source) + return text if translated == source else translated + text[len(prefix):] + return text + + +def explore(ui) -> None: + try: + _explore(ui) + except ConversionError as error: + raise ConversionError(conversion_message(error)) from error From 7213fb2553e618ab6c6a7d4a6bcd0036b25ed558 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 11:23:47 +0000 Subject: [PATCH 09/27] fix: clarify OCI update dialog and distinguish cancellation --- .github/scripts/run_offline_qualified.py | 1 + .../test_oci_recovery_inventory_wording.py | 2 +- .../tests/test_oci_update_cancellation.py | 166 ++++++++++++++++++ .../test_oci_update_preservation_wording.py | 11 +- .../tests/test_outcome_promises_wording.py | 6 +- AppImage/components/script-terminal-modal.tsx | 32 ++-- AppImage/components/virtual-machines.tsx | 6 + AppImage/lib/script-completion.ts | 14 ++ AppImage/messages/en/common.json | 1 + AppImage/messages/sk/common.json | 1 + lang/sk.json | 11 +- oci/src/proxmenux_oci/management.py | 19 +- tests/test_oci_update_cancellation.cjs | 111 ++++++++++++ 13 files changed, 357 insertions(+), 24 deletions(-) create mode 100644 .github/scripts/tests/test_oci_update_cancellation.py create mode 100644 AppImage/lib/script-completion.ts create mode 100644 tests/test_oci_update_cancellation.cjs diff --git a/.github/scripts/run_offline_qualified.py b/.github/scripts/run_offline_qualified.py index 8a9548cd..796cd962 100644 --- a/.github/scripts/run_offline_qualified.py +++ b/.github/scripts/run_offline_qualified.py @@ -28,6 +28,7 @@ PYTHON_FILES = ( "AppImage/scripts/tests/test_health_persistence_recurrence.py", ) NODE_FILES = ( + "tests/test_oci_update_cancellation.cjs", "tests/lxc_updates/test_local_app_icons_ui.cjs", "tests/lxc_updates/test_docker_app_discovery_ui.cjs", "tests/lxc_updates/test_native_app_discovery_ui.cjs", diff --git a/.github/scripts/tests/test_oci_recovery_inventory_wording.py b/.github/scripts/tests/test_oci_recovery_inventory_wording.py index 338698ae..097fc5c6 100644 --- a/.github/scripts/tests/test_oci_recovery_inventory_wording.py +++ b/.github/scripts/tests/test_oci_recovery_inventory_wording.py @@ -116,7 +116,7 @@ class RecoveryMessages(unittest.TestCase): instances.ROOT = Path('/inert') instances.read = lambda root, vmid: self.primary self.modules = {'oci_instances': instances, 'oci_stack_replay': ModuleType('oci_stack_replay')} - self.scope = {'sys': SimpleNamespace(path=[], executable='python3'), 'Path': Path, + self.scope = {'sys': SimpleNamespace(path=[], executable='python3'), 'Path': Path, 'CANCELLED': None, 'translate': lambda text: text, 'images': SimpleNamespace(offer_removal=lambda *args: None), '_remove': lambda *args: self.fail('remove must not run'), '_run_lifecycle': lambda command, title: self.calls.append((command, title)) or False} diff --git a/.github/scripts/tests/test_oci_update_cancellation.py b/.github/scripts/tests/test_oci_update_cancellation.py new file mode 100644 index 00000000..d06f5255 --- /dev/null +++ b/.github/scripts/tests/test_oci_update_cancellation.py @@ -0,0 +1,166 @@ +"""Real native OCI menu/CLI paths with all external effects replaced.""" +from contextlib import redirect_stdout +import copy +import io +import json +from pathlib import Path +import sys +import unittest +from unittest.mock import Mock, patch + +ROOT = Path(__file__).resolve().parents[3] +sys.path[:0] = [str(ROOT / 'oci/src'), str(ROOT / 'oci/remote')] +from proxmenux_oci import cli, i18n, management as menu, ui as native_ui, updates +import oci_instances as instances +import oci_instance_reconcile as reconcile + + +class UpdateCancellationTests(unittest.TestCase): + def setUp(self): + self.record = {'status': 'installed', 'vmid': 110, 'deployment': {}, 'template': {}} + self.original = copy.deepcopy(self.record) + self.row = dict(vmid=110, reason='matched', stack=False, pending=False, status='installed', + title='Fixture application', hostname='fixture', image='fixture:latest') + self.ui = Mock() + self.ui.unattended = False + self.ui.review.return_value = False + for module, name, options in [ + (i18n, '_language', dict(new='en')), (i18n, '_cache', dict(new={})), + (menu.os, 'geteuid', dict(return_value=0)), (menu.shutil, 'which', dict(return_value='/fixture/pct')), + (native_ui, 'interactive_ui', dict(return_value=self.ui)), + (menu, 'recover_automatically', dict(return_value=None)), + (menu, 'carry_records', dict(return_value={})), + (menu, 'check_selected', dict(side_effect=lambda project, row: row)), + (menu, 'saved_inventory', dict(return_value=[self.row])), + (menu, '_selected_configuration', dict(return_value=b'fixture configuration')), + (instances, 'read', dict(return_value=self.record)), + (reconcile, 'propose', dict(return_value=None)), + ]: + item = patch.object(module, name, **options) + item.start() + self.addCleanup(item.stop) + self.lifecycle = self.guard(menu, '_run_lifecycle') + self.backup = self.guard(menu, '_work_backup_ready') + self.write = self.guard(instances, 'write') + self.command = self.guard(instances, 'command') + self.commit = self.guard(reconcile, 'commit') + + def guard(self, module, name): + item = patch.object(module, name, side_effect=AssertionError('Forbidden effect: ' + name)) + mock = item.start() + self.addCleanup(item.stop) + return mock + + def no_effects(self): + for mock in [self.lifecycle, self.backup, self.write, self.command, self.commit]: + mock.assert_not_called() + self.assertEqual(self.record, self.original) + + def test_no_is_a_distinct_falsy_result_before_backup_or_lifecycle(self): + result = menu.manage_instance(ROOT / 'oci', self.ui, self.row, action='update') + self.assertIs(result, menu.CANCELLED) + self.assertFalse(result) + self.no_effects() + + def test_real_cli_no_returns_cancel_code_instead_of_failure_or_success(self): + with patch.object(cli, 'Catalog'), redirect_stdout(io.StringIO()) as output: + result = cli.main(['manage', '110', '--action', 'update']) + self.assertEqual(result, 6) + self.assertEqual(output.getvalue(), '') # The terminal renders the localized outcome once. + self.no_effects() + + def test_no_in_stack_confirmation_keeps_every_member_unchanged(self): + self.row['stack'] = True + self.record['stack'] = {'members': [{'vmid': 110}, {'vmid': 111}]} + self.original = copy.deepcopy(self.record) + with redirect_stdout(io.StringIO()): + result = menu.direct_management(ROOT / 'oci', 110, 'update') + self.assertEqual(result, 6) + self.no_effects() + + def test_native_menu_returns_to_its_list_after_no(self): + self.ui.choose.side_effect = ['110', 'update', None] + with patch.object(menu, '_clean_orphans'), patch.object(menu, 'offer_recovery'), \ + patch.object(menu, '_records_unread', return_value=[]): + menu.interactive_management(ROOT / 'oci', self.ui) + self.assertEqual(self.ui.choose.call_count, 3) + self.no_effects() + + def test_batch_no_does_not_start_the_next_application(self): + available = [{'vmid': 110, 'title': 'First'}, {'vmid': 111, 'title': 'Second'}] + rows = [self.row, {**self.row, 'vmid': 111}] + self.ui.checklist.return_value = ['all'] + self.ui.review.side_effect = [True, False] # Batch review, first application's confirmation. + with patch.object(updates, 'UpdateCheck') as checker, patch.object(menu, 'saved_inventory', return_value=rows), \ + patch.object(menu, 'check_selected', wraps=lambda project, row: row) as selected: + checker.return_value.status.return_value = {'available': available} + self.assertFalse(updates.select_updates(ROOT / 'oci', self.ui, available)) + self.assertEqual([call.args[1]['vmid'] for call in selected.call_args_list], [110]) + self.no_effects() + + def test_accepted_update_failure_keeps_failure_code_one(self): + self.ui.review.return_value = True + self.backup.side_effect = None + self.backup.return_value = False + self.assertEqual(menu.direct_management(ROOT / 'oci', 110, 'update'), 1) + self.lifecycle.assert_not_called() + self.write.assert_not_called() + + def test_accepted_update_preserves_success_and_existing_lifecycle_command(self): + self.ui.review.return_value = True + self.backup.side_effect = None + self.backup.return_value = True + self.lifecycle.side_effect = None + self.lifecycle.return_value = True + self.assertEqual(menu.direct_management(ROOT / 'oci', 110, 'update'), 0) + self.lifecycle.assert_called_once() + command = self.lifecycle.call_args.args[0] + self.assertEqual(command[:3], [sys.executable, str(ROOT / 'oci/remote/oci_update_current.py'), '110']) + self.assertIn('--acknowledge-external-data', command) + self.write.assert_not_called() + + def test_actual_lifecycle_failure_remains_failure_after_positive_confirmation(self): + self.ui.review.return_value = True + self.backup.side_effect = None + self.backup.return_value = True + self.lifecycle.side_effect = None + self.lifecycle.return_value = False + self.assertEqual(menu.direct_management(ROOT / 'oci', 110, 'update'), 1) + self.lifecycle.assert_called_once() + self.write.assert_not_called() + + def test_unattended_positive_default_still_reaches_existing_update(self): + self.backup.side_effect = None + self.backup.return_value = True + self.lifecycle.side_effect = None + self.lifecycle.return_value = True + self.assertEqual(menu.direct_management(ROOT / 'oci', 110, 'update', unattended=True), 0) + self.lifecycle.assert_called_once() + self.write.assert_not_called() + + def test_existing_unattended_review_age_busy_and_missing_codes_are_preserved(self): + with patch.object(reconcile, 'propose', return_value={'details': ['external change']}), redirect_stdout(io.StringIO()): + self.assertEqual(menu.direct_management(ROOT / 'oci', 110, 'update', unattended=True), 4) + with patch.object(menu, '_image_too_recent', return_value=True), redirect_stdout(io.StringIO()): + self.assertEqual(menu.direct_management(ROOT / 'oci', 110, 'update', unattended=True, min_image_age_days=3), 5) + with patch.object(menu, 'saved_inventory', return_value=[]): + self.assertEqual(menu.direct_management(ROOT / 'oci', 110, 'update'), 2) + with patch.object(menu, 'recover_automatically', side_effect=BlockingIOError()): + self.assertEqual(menu.direct_management(ROOT / 'oci', 110, 'update'), 3) + self.no_effects() + + def test_slovak_dialog_and_recovery_choice_have_the_approved_meaning(self): + cache = json.loads((ROOT / 'lang/sk.json').read_text()) + with patch.object(i18n, '_language', 'sk'), patch.object(i18n, '_cache', cache): + menu.manage_instance(ROOT / 'oci', self.ui, self.row, action='update') + self.assertEqual(i18n.translate('Recover the previous installation'), 'Obnoviť predchádzajúcu inštaláciu') + text = self.ui.review.call_args.args[0] + for part in ['novší obraz aplikácie', 'overí sa jej integrita', 'pokúsi obnoviť', 'Pripojené adresáre hostiteľa']: + self.assertIn(part, text) + self.assertNotIn('kanál obrazov', text) + self.assertEqual(self.ui.review.call_args.kwargs['question'], 'Spustiť aktualizáciu?') + self.no_effects() + + +if __name__ == '__main__': + unittest.main() diff --git a/.github/scripts/tests/test_oci_update_preservation_wording.py b/.github/scripts/tests/test_oci_update_preservation_wording.py index e3fdae92..448b4832 100644 --- a/.github/scripts/tests/test_oci_update_preservation_wording.py +++ b/.github/scripts/tests/test_oci_update_preservation_wording.py @@ -24,9 +24,12 @@ OLD_GUARD = 'The current template changes the image or identity; an explicit mig GUARD = 'The current template changes the template identity or image repository; an explicit migration is required' OLD_PREVIEW = ('The saved image channel is checked for a newer image. If replacement is needed, the CT is stopped and ' 'a native backup is verified before its root is replaced. Host directories are outside that backup.') -PREVIEW = ('If the image channel has a new version, the CT is stopped, backed up and verified, and replaced with ' - 'the new image to update the container. If the update fails after the container was changed, the backup ' - 'is restored. An update that stops halfway is recovered from the OCI management menu.') +PREVIEW = ('If a newer application image is available, the container is stopped and backed up, and the backup ' + 'integrity is verified. The container is then updated using the new image.\n\n' + 'If the update fails after the container was changed, ProxMenux attempts to restore the previous ' + 'installation from the backup. If the operation remains incomplete, select the application in OCI ' + 'management and choose "Recover the previous installation".\n\n' + 'Host directories mounted in the container are not restored by the container backup.') def extracted(path, name, scope): @@ -130,7 +133,7 @@ class UpdateWording(unittest.TestCase): reconcile = ModuleType('oci_instance_reconcile') reconcile.propose = lambda *args: None scope = {'sys': SimpleNamespace(path=[], executable='python3'), - 'os': os, 'tempfile': tempfile, 'Path': Path, + 'os': os, 'tempfile': tempfile, 'Path': Path, 'CANCELLED': None, 'translate': lambda text: text, 'check_selected': lambda project, row: row, '_selected_configuration': lambda vmid: instances.command('pct', 'config', str(vmid)), 'images': SimpleNamespace(offer_removal=lambda *args: None), diff --git a/.github/scripts/tests/test_outcome_promises_wording.py b/.github/scripts/tests/test_outcome_promises_wording.py index 2b793640..29e70f9e 100644 --- a/.github/scripts/tests/test_outcome_promises_wording.py +++ b/.github/scripts/tests/test_outcome_promises_wording.py @@ -20,7 +20,11 @@ class OutcomePromises(TestCase): texts = oci_menu_texts() self.assertEqual([text for text in texts if 'If anything fails' in text], []) previews = [text for text in texts if 'stops halfway is recovered from the OCI management menu' in text] - self.assertEqual(len(previews), 3) + self.assertEqual(len(previews), 2) # Coordinated stack update/recreation previews. + update = next(text for text in texts if text.startswith('If a newer application image is available')) + self.assertIn('attempts to restore the previous installation', update) + self.assertIn('If the operation remains incomplete', update) + self.assertIn('choose "Recover the previous installation"', update) def test_the_recovery_preview_says_when_each_step_happens(self): preview = next(text for text in oci_menu_texts() if 'previous native backup' in text) diff --git a/AppImage/components/script-terminal-modal.tsx b/AppImage/components/script-terminal-modal.tsx index c993281d..76f90669 100644 --- a/AppImage/components/script-terminal-modal.tsx +++ b/AppImage/components/script-terminal-modal.tsx @@ -31,6 +31,7 @@ import { import "xterm/css/xterm.css" import { API_PORT } from "@/lib/api-config" import { getTicketedWsUrl } from "@/lib/terminal-ws" +import { scriptCompletion } from "@/lib/script-completion" import { useT } from "../lib/i18n/provider" interface WebInteraction { @@ -52,6 +53,7 @@ interface ScriptTerminalModalProps { params?: Record completedSuccessfullyMessage?: string completedWithErrorMessage?: (exitCode: number) => string + cancelledExitCode?: number // Optional callback fired when the script's WebSocket closes // (script_runner sends an exit code and then closes). Lets the // parent auto-dismiss the modal — used by host-backup's Restore @@ -69,6 +71,7 @@ export function ScriptTerminalModal({ params = { EXECUTION_MODE: "web" }, completedSuccessfullyMessage, completedWithErrorMessage, + cancelledExitCode, onComplete, }: ScriptTerminalModalProps) { const t = useT() @@ -103,10 +106,12 @@ export function ScriptTerminalModal({ const getCompletionMessage = useCallback( (exitCode: number) => - exitCode === 0 + scriptCompletion(exitCode, cancelledExitCode).kind === "cancelled" + ? t("scriptTerminal.cancelled") + : exitCode === 0 ? (completedSuccessfullyMessage ?? t("scriptTerminal.completedSuccessfully")) : (completedWithErrorMessage?.(exitCode) ?? t("scriptTerminal.completedWithError", { code: exitCode })), - [completedSuccessfullyMessage, completedWithErrorMessage, t], + [completedSuccessfullyMessage, completedWithErrorMessage, cancelledExitCode, t], ) const terminalContainerRef = useRef(null) @@ -139,6 +144,11 @@ export function ScriptTerminalModal({ reconnectTimeoutRef.current = setTimeout(async () => { if (wsRef.current?.readyState !== WebSocket.OPEN && termRef.current) { if (wsRef.current) { + // Replacing a stale socket is not a script result. Its close event + // must not complete the operation before the new socket reports it. + wsRef.current.onclose = null + wsRef.current.onerror = null + wsRef.current.onmessage = null wsRef.current.close() } @@ -176,6 +186,7 @@ const initMessage = { } ws.onmessage = (event) => { + if (completionReceivedRef.current) return // Filter out pong responses from heartbeat if (event.data === '{"type": "pong"}' || event.data === '{"type":"pong"}') { return @@ -189,10 +200,10 @@ const initMessage = { : null if (exitMatch) { const exitCode = Number(exitMatch[1]) - termRef.current?.write(event.data) + if (scriptCompletion(exitCode, cancelledExitCode).kind !== "cancelled") termRef.current?.write(event.data) completionReceivedRef.current = true setIsComplete(true) - termRef.current?.writeln(`\x1b[${exitCode === 0 ? "32" : "31"}m${getCompletionMessage(exitCode)}\x1b[0m`) + termRef.current?.writeln(`\x1b[${scriptCompletion(exitCode, cancelledExitCode).color}m${getCompletionMessage(exitCode)}\x1b[0m`) onCompleteRef.current?.(exitCode) if (ws.readyState === WebSocket.OPEN) ws.close(1000, "script complete") return @@ -223,7 +234,7 @@ const initMessage = { const exitCode = Number(msg.exit_code ?? 1) completionReceivedRef.current = true setIsComplete(true) - termRef.current?.writeln(`\x1b[${exitCode === 0 ? "32" : "31"}m${getCompletionMessage(exitCode)}\x1b[0m`) + termRef.current?.writeln(`\x1b[${scriptCompletion(exitCode, cancelledExitCode).color}m${getCompletionMessage(exitCode)}\x1b[0m`) onCompleteRef.current?.(exitCode) if (ws.readyState === WebSocket.OPEN) ws.close(1000, "script complete") return @@ -261,7 +272,7 @@ const initMessage = { } } }, 1000) - }, [isOpen, isComplete, scriptPath, getCompletionMessage, t]) + }, [isOpen, isComplete, scriptPath, getCompletionMessage, cancelledExitCode, t]) const sendKey = useCallback((key: string) => { if (!termRef.current) return @@ -392,6 +403,7 @@ const initMessage = { } ws.onmessage = (event) => { + if (completionReceivedRef.current) return // Filter out pong responses from heartbeat - don't display in terminal if (event.data === '{"type": "pong"}' || event.data === '{"type":"pong"}') { return @@ -405,10 +417,10 @@ const initMessage = { : null if (exitMatch) { const exitCode = Number(exitMatch[1]) - term.write(event.data) + if (scriptCompletion(exitCode, cancelledExitCode).kind !== "cancelled") term.write(event.data) completionReceivedRef.current = true setIsComplete(true) - term.writeln(`\x1b[${exitCode === 0 ? "32" : "31"}m${getCompletionMessage(exitCode)}\x1b[0m`) + term.writeln(`\x1b[${scriptCompletion(exitCode, cancelledExitCode).color}m${getCompletionMessage(exitCode)}\x1b[0m`) onCompleteRef.current?.(exitCode) if (ws.readyState === WebSocket.OPEN) ws.close(1000, "script complete") return @@ -441,7 +453,7 @@ const initMessage = { const exitCode = Number(msg.exit_code ?? 1) completionReceivedRef.current = true setIsComplete(true) - term.writeln(`\x1b[${exitCode === 0 ? "32" : "31"}m${getCompletionMessage(exitCode)}\x1b[0m`) + term.writeln(`\x1b[${scriptCompletion(exitCode, cancelledExitCode).color}m${getCompletionMessage(exitCode)}\x1b[0m`) onCompleteRef.current?.(exitCode) if (ws.readyState === WebSocket.OPEN) ws.close(1000, "script complete") return @@ -814,7 +826,7 @@ const initMessage = { )} {(isMobile || isTablet) && ( -
+
)} diff --git a/AppImage/messages/en/common.json b/AppImage/messages/en/common.json index 5685983b..a7fe7cb2 100644 --- a/AppImage/messages/en/common.json +++ b/AppImage/messages/en/common.json @@ -1327,10 +1327,11 @@ "update": "Update", "modify": "Modify", "recover": "Recover", + "registerRestored": "Register again", "terminalTitleUpdate": "Update OCI application", "terminalTitleRecoverPrevious": "Recover the previous OCI installation", "terminalTitleModify": "Modify OCI application", - "terminalTitleRecover": "Recover restored OCI application", + "terminalTitleRecover": "Register restored OCI applications", "terminalDescription": "Runs the same process as the ProxMenux menu: OCI manager Apps → Manage installed OCI applications.", "keepBackup": "Keep the backup taken before updating", "noKeepBackup": "The backup taken before updating is not kept", @@ -1561,7 +1562,7 @@ "ociTitle": "OCI image container", "ociBody": "This container was created from an OCI image outside OCI manager Apps. It is updated by replacing its image, which ProxMenux does not manage for it.", "ociRestoredTitle": "Restored OCI application", - "ociRestoredBody": "This container was restored from a backup and this host has no record of its application, so it cannot be updated from here yet. Recover registers it again on this host, the same as OCI manager Apps → Manage installed OCI applications.", + "ociRestoredBody": "This container was restored from a backup and this host has no record of its OCI application, so it cannot be updated here yet. Register again opens the registration of restored OCI applications on this node, as in OCI manager Apps → Manage installed OCI applications.", "dockerImagesTitle": "Docker images", "dockerAppTitle": "Docker", "dockerImagesSubheading": "Images", diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index b84445c8..b9d2498d 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -1252,10 +1252,11 @@ "update": "Aktualizovať", "modify": "Upraviť", "recover": "Obnoviť", + "registerRestored": "Znova zaregistrovať", "terminalTitleUpdate": "Aktualizovať OCI aplikáciu", "terminalTitleRecoverPrevious": "Obnoviť predchádzajúcu OCI inštaláciu", "terminalTitleModify": "Upraviť OCI aplikáciu", - "terminalTitleRecover": "Obnoviť obnovenú OCI aplikáciu", + "terminalTitleRecover": "Zaregistrovať OCI aplikácie obnovené zo zálohy", "terminalDescription": "Použije sa rovnaký postup ako v ponuke Správca OCI aplikácií → Spravovať nainštalované OCI aplikácie.", "keepBackup": "Ponechať zálohu vytvorenú pred aktualizáciou", "noKeepBackup": "Záloha vytvorená pred aktualizáciou sa neponecháva", @@ -1486,7 +1487,7 @@ "ociTitle": "Kontajner z OCI obrazu", "ociBody": "Tento kontajner bol vytvorený z OCI (Docker) obrazu mimo Správcu OCI aplikácií. Aktualizuje sa nahradením obrazu, čo ProxMenux pri tomto kontajneri nespravuje.", "ociRestoredTitle": "Obnovená OCI aplikácia", - "ociRestoredBody": "Tento kontajner bol obnovený zo zálohy a tento hostiteľ nemá záznam o jeho aplikácii, preto ho zatiaľ nemožno aktualizovať odtiaľto. Obnoviť ho znova zaregistruje na tomto hostiteľovi, rovnako ako OCI manager Apps → Spravovať nainštalované OCI aplikácie.", + "ociRestoredBody": "Tento kontajner bol obnovený zo zálohy, ale tento server nemá záznam o jeho OCI aplikácii, preto ju tu zatiaľ nemožno aktualizovať. Možnosť Znova zaregistrovať otvorí registráciu obnovených OCI aplikácií na tomto uzle, rovnako ako ponuka Správca OCI aplikácií → Spravovať nainštalované OCI aplikácie.", "dockerImagesTitle": "Docker obrazy", "dockerAppTitle": "Docker", "dockerImagesSubheading": "Obrazy", diff --git a/lang/sk.json b/lang/sk.json index 2f56591f..3b96e7db 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -903,7 +903,7 @@ "Choose an action:": "Vyberte akciu:", "Choose an export to mount:": "Vyberte export, ktorý chcete pripojiť:", "Choose an option:": "Vyberte možnosť:", - "Choose another bridge for the container in Proxmox (Network) and run the recovery again.": "Vyberte iný most pre kontajner v Proxmox (Sieť) a znova spustite obnovu.", + "Choose another bridge for the container in Proxmox (Network) and run the recovery again.": "V Proxmoxe vyberte pre kontajner iný sieťový most (Sieť) a registráciu zopakujte.", "Choose any host path you want to share with CTs.": "Vyberte ľubovoľnú cestu na serveri, ktorú chcete zdieľať s CT.", "Choose conflict policy": "Vyberte, ako vyriešiť konflikt", "Choose conflict policy for the source VM:": "Vyberte pravidlo riešenia konfliktu pre zdrojovú VM:", @@ -1143,7 +1143,7 @@ "Conflicting path included in backup:": "Konfliktná cesta zahrnutá v zálohe:", "Conflicting utilities removed": "Konfliktné nástroje odstránené", "Connect a Coral Accelerator and try again.": "Pripojte Coral Accelerator a skúste to znova.", - "Connect it, or remove it from the container in Proxmox (Resources), and run the recovery again.": "Pripojte ho alebo ho odstráňte z kontajnera v Proxmox (zdroje) a znova spustite obnovenie.", + "Connect it, or remove it from the container in Proxmox (Resources), and run the recovery again.": "Pripojte zariadenie alebo ho odstráňte z konfigurácie kontajnera v Proxmoxe (Zdroje) a registráciu zopakujte.", "Connect your devices and users together in your own secure virtual private network.": "Spojte svoje zariadenia a užívateľov vo vlastnej zabezpečenej virtuálnej súkromnej sieti.", "Connect your devices into a secure WireGuard®-based overlay network with SSO, MFA and granular access controls.": "Pripojte svoje zariadenia do zabezpečenej prekryvnej siete založenej na WireGuard® s ovládacími prvkami SSO, MFA a granulovaného prístupu.", "Connected": "Pripojené", @@ -3789,7 +3789,7 @@ "Mount name": "Názov pripojenia", "Mount not authorized by the operation": "Pripojiť nie je povolené operation", "Mount options:": "Možnosti pripojenia:", - "Mount or create it with its data and run the recovery again.": "Pripojte alebo vytvorte ho s jeho údajmi a znova spustite obnovenie.", + "Mount or create it with its data and run the recovery again.": "Pripojte alebo vytvorte adresár s potrebnými údajmi a registráciu zopakujte.", "Mount path must be an absolute path starting with /": "Cesta pripojenia musí byť absolútna a začínať znakom /", "Mount path:": "Cesta pripojenia:", "Mount paths must not overlap": "Pripojiť cesty sa nesmú prekrývať", @@ -4302,7 +4302,7 @@ "No recent Samba servers found.": "Nenašli sa žiadne nedávne Samba servery.", "No registered OCI containers are available for selection on this host.": "Na tomto hostiteľovi nie sú dostupné žiadne registrované kontajnery OCI.", "No registry API was found at {registry}. Check the hostname and port.": "Na {registry} sa nenašlo žiadne API registra. Skontrolujte názov hostiteľa a port.", - "No restored OCI application is waiting to be recovered.": "Žiadna obnovená aplikácia OCI nečaká na obnovenie.", + "No restored OCI application is waiting to be recovered.": "Žiadna OCI aplikácia obnovená zo zálohy nečaká na opätovnú registráciu.", "No results": "Žiadne výsledky", "No routing information found.": "Nenašli sa žiadne informácie o smerovaní.", "No scheduled backup jobs configured.": "Nie sú nastavené žiadne naplánované úlohy záloh.", @@ -5068,10 +5068,10 @@ "Recover OCI stack": "Obnoviť OCI aplikáciu s viacerými kontajnermi", "Recover now?": "Obnoviť teraz?", "Recover or complete the operation?": "Obnoviť alebo dokončiť operáciu?", - "Recover restored OCI applications": "Obnovte obnovené aplikácie OCI", + "Recover restored OCI applications": "Registrácia obnovených OCI aplikácií", "Recover the keyfile using your recovery passphrase?": "Obnoviť keyfile pomocou obnovovacej frázy?", "Recover the previous installation": "Obnoviť predchádzajúcu inštaláciu", - "Recover them now?": "Obnoviť ich teraz?", + "Recover them now?": "Znova zaregistrovať uvedené aplikácie?", "Recoverable:": "Obnoviteľné:", "Recovering the previous installation": "Obnovovanie predchádzajúcej inštalácie", "Recovery blob upload failed — main backup is OK, but keyfile recovery from PBS will not be available for this backup.": "Nahratie obnovovacieho balíčka zlyhalo - hlavná záloha je v poriadku, ale obnova keyfile z PBS pre túto zálohu nebude dostupná.", @@ -6021,7 +6021,7 @@ "Start the LXC when finished to apply the selected configuration?": "Spustiť LXC po dokončení pre použitie zvolenej konfigurácie?", "Start the VM": "Spustiť VM", "Start the VM to begin Windows installation from the mounted ISO.": "Spustite VM a začnite inštaláciu Windowsu z pripojeného ISO.", - "Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Spustiť aplikácie po ich zaregistrovaní? Odpoveď Nie, ak pôvodné kontajnery stále bežia na inom hostiteľovi: oba by používali rovnaké adresy.", + "Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Spustiť aplikácie po ich opätovnej registrácii? Ak pôvodné kontajnery stále bežia na inom serveri, vyberte Nie. Obe inštalácie by používali rovnaké adresy.", "Start the converted container:": "Spustiť prevedený kontajner:", "Start the interactive Proxmox package upgrade": "Spustiť interaktívnu aktualizáciu balíkov Proxmoxu", "Start the main system upgrade:": "Spustite hlavnú aktualizáciu systému:", @@ -6344,7 +6344,7 @@ "The all-in-one AI application.": "Aplikácia UI.", "The application configuration needs a first start to complete.": "Konfigurácia aplikácie potrebuje prvý štart na dokončenie.", "The application could not be modified:": "Aplikáciu nebolo možné upraviť:", - "The application could not be recovered:": "Aplikáciu nebolo možné obnoviť:", + "The application could not be recovered:": "Aplikáciu sa nepodarilo znova zaregistrovať:", "The application could not be recreated:": "Aplikáciu nebolo možné znova vytvoriť:", "The application did not complete its initial setup:": "Žiadosť nevyplnila svoje pôvodné nastavenie:", "The application did not get an address on the access network:": "Aplikácia nedostala adresu v prístupovej sieti:", @@ -6561,7 +6561,7 @@ "The kernel module is not active:": "Modul jadra nie je aktívny:", "The kernel module of version": "Modul jadra verzie", "The local envelope is dropped and future backups do not upload anything. Uploaded envelopes already on PBS stay intact and remain recoverable with their original passphrase.": "Lokálna obálka sa zahodí a budúce zálohy už nič nenahrajú. Obálky, ktoré už sú v PBS, zostanú zachované a budú obnoviteľné pôvodnou frázou.", - "The local storage does not accept snippets": "Miestne úložisko neprijíma úryvky", + "The local storage does not accept snippets": "Úložisko local nepodporuje obsah typu snippets", "The long test runs directly on the disk hardware.": "Dlhý test beží priamo na hardvéri disku.", "The machine learning container is rebuilt with the image of the new choice. Its model cache, the library and the database are kept. The whole application is stopped and updated, as in an update. If the operation fails after a container was changed, the previous containers and the previous choice are restored. An operation that stops halfway is recovered from the OCI management menu.": "Kontajner strojového učenia sa znova vytvorí z obrazu pre zvolený CPU alebo GPU. Vyrovnávacia pamäť modelov, knižnica a databáza sa zachovajú. Celá aplikácia sa zastaví a aktualizuje rovnakým postupom ako pri bežnej aktualizácii. Ak operácia po zmene kontajnera zlyhá, ProxMenux sa pokúsi obnoviť predchádzajúce kontajnery aj pôvodnú voľbu CPU/GPU. Ak operácia zostane nedokončená, obnovte ju z ponuky správy OCI.", "The machine learning container is rebuilt with the image of the new choice. Its model cache, the library and the database are kept. The whole application is stopped and updated, as in an update; if anything fails, the previous containers and the previous choice are restored.": "Kontajner strojového učenia je prestavaný s obrazom novej voľby. Uchováva sa jej vyrovnávacia pamäť modelu, knižnica a databáza. Celá aplikácia sa zastaví a aktualizuje ako pri aktualizácii; ak čokoľvek zlyhá, obnovia sa predchádzajúce kontajnery a predchádzajúca voľba.", @@ -6846,7 +6846,7 @@ "This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Táto VM vyžaduje ďalšie kroky inštalácie. Návod nájdete tu:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144", "This account is for {registry}. Enter an image from that registry.": "Tento účet patrí registru {registry}. Zadajte obraz z tohto registra.", "This action will:": "Táto akcia vykoná:", - "This application cannot be recovered yet:": "Túto aplikáciu zatiaľ nie je možné obnoviť:", + "This application cannot be recovered yet:": "Túto aplikáciu zatiaľ nemožno znova zaregistrovať:", "This application cannot be recovered yet; nothing was changed:": "Túto aplikáciu zatiaľ nie je možné obnoviť; nič sa nezmenilo:", "This application is not an Immich installed by ProxMenux": "Táto aplikácia nie je Immich nainštalovaný pomocou ProxMenuxu", "This application is not in the OCI catalog.": "Táto aplikácia nie je v katalógu OCI.", @@ -7905,12 +7905,12 @@ "is referenced in the following stopped VM(s)/CT(s):": "je uvedený v týchto zastavených VM/CT:", "it asks for the network of the host; the container gets its own address instead": "žiada o sieť hostiteľa; kontajner dostane namiesto toho svoju vlastnú adresu", "it could not be started:": "nedalo sa spustiť:", - "it is locked or its disk could not be mounted": "je zamknutá alebo sa jej disk nepodarilo pripojiť", + "it is locked or its disk could not be mounted": "je uzamknutý alebo sa nepodarilo pripojiť jeho disk", "it publishes no other architecture": "nepublikuje žiadnu inú architektúru", - "it uses an NVIDIA GPU and this host has no working NVIDIA driver and Container Toolkit. Install them and run the recovery again.": "používa NVIDIA GPU a tento hostiteľ nemá funkčný ovládač NVIDIA a súpravu kontajnerových nástrojov. Nainštalujte ich a znova spustite obnovenie.", + "it uses an NVIDIA GPU and this host has no working NVIDIA driver and Container Toolkit. Install them and run the recovery again.": "používa GPU NVIDIA, ale na tomto serveri nie je funkčný ovládač NVIDIA alebo NVIDIA Container Toolkit. Nainštalujte chýbajúce súčasti a registráciu zopakujte.", "it uses the Compose option": "používa možnosť Compose", "its NVIDIA runtime could not be rebuilt for the driver of this host; the container will not start until it is installed again:": "jeho runtime NVIDIA nebolo možné prebudovať pre ovládač tohto hostiteľa; kontajner sa nespustí, kým nebude znova nainštalovaný:", - "its Rclone mount is published again on this host at": "jeho pripojenie Rclone je opäť zverejnené na tomto hostiteľovi na adrese", + "its Rclone mount is published again on this host at": "jeho pripojenie Rclone sa na tomto serveri opäť sprístupní v ceste", "its Rclone mount is published by a host script that a backup does not include. Enable the mount again from the Rclone entry of the catalog before starting it.": "jeho pripojenie Rclone je publikované hostiteľským skriptom, ktorý záloha neobsahuje. Pred spustením znova povoľte pripojenie v položke Rclone v katalógu.", "its backup was made before ProxMenux kept the record inside the container": "jeho záloha bola vykonaná skôr, ako ProxMenux uchovával záznam v kontajneri", "its configuration could not be read": "nepodarilo sa prečítať jeho konfiguráciu", @@ -8123,7 +8123,7 @@ "systemctl restart networking failed:": "systemctl restart networking zlyhal:", "systemd OnCalendar expression": "systemd OnCalendar výraz", "the API key was not generated on the first start": "kľúč API nebol vytvorený pri prvom štarte", - "the NVIDIA hook of the container is not available:": "hák NVIDIA kontajnera nie je k dispozícii:", + "the NVIDIA hook of the container is not available:": "spúšťací skript NVIDIA pre tento kontajner nie je dostupný:", "the bridge does not exist on this host:": "most na tomto hostiteľovi neexistuje:", "the bridge exists on this host with another network:": "most existuje na tomto hostiteľovi s inou sieťou:", "the container has its own address, so the port Docker published on the host is not needed": "kontajner má svoju vlastnú adresu, takže port Docker zverejnené na hostiteľa nie je potrebné", diff --git a/oci/src/proxmenux_oci/management.py b/oci/src/proxmenux_oci/management.py index 3f3b0bcd..162bdf4f 100644 --- a/oci/src/proxmenux_oci/management.py +++ b/oci/src/proxmenux_oci/management.py @@ -229,7 +229,7 @@ def offer_recovery(project, ui): f"{translate('Until they are registered again they cannot be updated or managed from here. The recovery checks first what prevents their registration; an application with such a problem is not registered. A Rclone mount that has to be enabled again does not prevent it, but then the applications are not started automatically.')}" f"\n\n{translate('Recover them now?')}") if not ui.confirm(text, default=True): - return + return CANCELLED command = [sys.executable, str(project / 'remote/oci_restore_recovery.py'), 'recover'] for rule in _restored_firewall_rules(project): if ui.confirm(translate('CT {vmid} is a host monitor and had a rule in the host firewall. Allow TCP port {port} from {subnet} through the firewall of this host? Existing firewall rules are not changed.').format( @@ -252,7 +252,9 @@ def direct_recovery(project): if not restored_applications(project): ui.message(translate('No restored OCI application is waiting to be recovered.'), translate('Restored OCI applications')) return EXIT_DONE - offer_recovery(project, ui) + completed = offer_recovery(project, ui) + if completed is CANCELLED: + return EXIT_CANCELLED return EXIT_DONE if not restored_applications(project) else EXIT_FAILED diff --git a/tests/test_oci_update_cancellation.cjs b/tests/test_oci_update_cancellation.cjs index 21749c3a..b91e77e2 100644 --- a/tests/test_oci_update_cancellation.cjs +++ b/tests/test_oci_update_cancellation.cjs @@ -69,7 +69,7 @@ const fs=require('node:fs'); assert.match(fs.readFileSync('oci/src/proxmenux_oci/management.py','utf8'),/^EXIT_CANCELLED = 6 /m); assert.equal(completion.OCI_CANCELLED_EXIT_CODE,6); const parent=ts.createSourceFile('vms.tsx',read('components/virtual-machines.tsx'),ts.ScriptTarget.Latest,true,ts.ScriptKind.TSX); -let ociElement,reset,pendingRecoveryClick; +let ociElement,reset,pendingRecoveryClick,registrationClick,registrationLabel; function find(node) { if(ts.isJsxSelfClosingElement(node) && node.tagName.getText(parent)==='ScriptTerminalModal' && node.attributes.properties.some(p=>p.name?.getText(parent)==='scriptPath' && p.initializer?.text?.endsWith('/oci/manage_instance.sh'))) ociElement=node; @@ -77,13 +77,30 @@ function find(node) { if(ts.isJsxElement(node) && node.openingElement.tagName.getText(parent)==='Button') { const click=node.openingElement.attributes.properties.find(p=>p.name?.getText(parent)==='onClick')?.initializer?.expression; if(click?.getText(parent).includes('setOciAction(') && click.getText(parent).includes('action: "update"')) pendingRecoveryClick=click; + if(click?.getText(parent).includes('setOciAction(') && click.getText(parent).includes('action: "recover"')) { + registrationClick=click; + registrationLabel=node.children.find(child=>ts.isJsxExpression(child)&&child.expression)?.expression; + } } ts.forEachChild(node,find); } -find(parent);assert.ok(ociElement);assert.ok(reset);assert.ok(pendingRecoveryClick); +find(parent);assert.ok(ociElement);assert.ok(reset);assert.ok(pendingRecoveryClick);assert.ok(registrationClick);assert.ok(registrationLabel); const attribute=name=>ociElement.attributes.properties.find(p=>p.name?.getText(parent)===name)?.initializer?.expression; assert.equal(attribute('cancelledExitCode').getText(parent),'OCI_CANCELLED_EXIT_CODE'); (async()=>{ + for(const locale of ['en','sk']) { + const scope={t:translator(locale),selectedVM:{vmid:110},applyBackup:false,applyBackupStorage:'',selectedBackupStorage:''}; + scope.setOciAction=value=>scope.ociAction=value; + executable(registrationClick,parent,scope)(); + assert.equal(scope.ociAction.action,'recover');assert.equal(scope.ociAction.vmid,110); + assert.equal(executable(registrationLabel,parent,scope),locale==='sk'?'Znova zaregistrovať':'Register again'); + assert.equal(executable(attribute('title'),parent,scope),locale==='sk'? + 'Zaregistrovať OCI aplikácie obnovené zo zálohy':'Register restored OCI applications'); + scope.selectedVM={vmid:111}; + assert.equal(executable(attribute('params'),parent,scope).VMID,'110','registration preserves the clicked target context'); + assert.equal(executable(attribute('params'),parent,scope).ACTION,'recover','registration retains its existing global CLI action'); + assert.equal(scope.t('vmLxc.ociUpdates.recover'),locale==='sk'?'Obnoviť':'Recover'); + } for(const locale of ['en','sk']) for(const pending of [false,true]) { const scope={t:translator(locale), selectedVM:{vmid:110}, ociInstance:{pending}, applyBackup:false,applyBackupStorage:'',selectedBackupStorage:''}; From d5f4691be7e299257cffb176cecfc13aec0a9641 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 16:58:44 +0000 Subject: [PATCH 17/27] i18n: clarify registration prerequisite checks in initial dialog --- .github/scripts/tests/test_oci_registration_sk.py | 12 ++++++++++++ lang/sk.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/scripts/tests/test_oci_registration_sk.py b/.github/scripts/tests/test_oci_registration_sk.py index 577fff61..ce4cb4ca 100644 --- a/.github/scripts/tests/test_oci_registration_sk.py +++ b/.github/scripts/tests/test_oci_registration_sk.py @@ -144,6 +144,18 @@ class RegistrationCancellationTests(RegistrationFixture): 'Žiadna OCI aplikácia obnovená zo zálohy nečaká na opätovnú registráciu.') self.no_effects() + def test_initial_single_and_multi_dialog_describes_registration_precheck(self): + catalog=json.loads((ROOT/'lang/sk.json').read_text()) + for multi in (False,True): + if multi:self.members() + with self.subTest(multi=multi),patch.object(i18n,'_language','sk'),patch.object(i18n,'_cache',catalog): + self.assertEqual(menu.direct_recovery(ROOT/'oci'),6) + text=self.ui.confirm.call_args.args[0] + self.assertIn('Pred opätovnou registráciou',text) + self.assertNotIn('Obnova najprv',text) + self.assertIn('aplikácie sa potom nespúšťajú automaticky',text) + self.no_effects() + def test_real_backend_blocker_stays_error_and_uses_registration_wording(self): catalog=json.loads((ROOT/'lang/sk.json').read_text()) plan={'title':'Fictional restored application','restored':[{'vmid':110}], diff --git a/lang/sk.json b/lang/sk.json index 3b96e7db..d395a92a 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -7189,7 +7189,7 @@ "Unsupported volume options:": "Nepodporované možnosti objemu:", "Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host and changes nothing otherwise.": "Kým nie sú znova zaregistrované, nemožno ich aktualizovať ani spravovať odtiaľto. Obnova najprv skontroluje, či všetko, čo potrebujú, je na tomto hostiteľovi a inak nič nezmení.", "Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host; an application that lacks something is not registered.": "Kým nie sú znova zaregistrované, nemožno ich aktualizovať ani spravovať odtiaľto. Obnova najprv skontroluje, či všetko, čo potrebujú, je na tomto hostiteľovi; aplikácia, ktorej niečo chýba, nie je zaregistrovaná.", - "Until they are registered again they cannot be updated or managed from here. The recovery checks first what prevents their registration; an application with such a problem is not registered. A Rclone mount that has to be enabled again does not prevent it, but then the applications are not started automatically.": "Kým nie sú znova zaregistrované, nemožno ich aktualizovať ani spravovať odtiaľto. Obnova najprv skontroluje, čo bráni ich registrácii; aplikácia s takýmto problémom nie je zaregistrovaná. Pripojenie Rclone, ktoré je potrebné znova povoliť, tomu nebráni, ale aplikácie sa potom nespúšťajú automaticky.", + "Until they are registered again they cannot be updated or managed from here. The recovery checks first what prevents their registration; an application with such a problem is not registered. A Rclone mount that has to be enabled again does not prevent it, but then the applications are not started automatically.": "Kým nie sú znova zaregistrované, nemožno ich odtiaľto aktualizovať ani spravovať. Pred opätovnou registráciou sa skontroluje, čo jej bráni. Aplikácia s takýmto problémom sa nezaregistruje. Pripojenie Rclone, ktoré treba znova povoliť, registrácii nebráni, ale aplikácie sa potom nespúšťajú automaticky.", "Untrusted or modified NVIDIA hook": "Nespoľahlivý alebo modifikovaný háčik NVIDIA", "Unused image removed from the cache:": "Nepoužívaný obraz odstránený z vyrovnávacej pamäte:", "Unused images removed from the cache:": "Nepoužité obrázky odstránené z vyrovnávacej pamäte:", From 37ad54232a74fde9ae84ebac80245139d61d1227 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 17:29:45 +0000 Subject: [PATCH 18/27] i18n: clarify coordinated OCI update and recreation in Slovak --- .../scripts/tests/test_oci_stack_update_sk.py | 196 ++++++++++++++++++ lang/sk.json | 114 +++++----- oci/src/proxmenux_oci/management.py | 2 +- 3 files changed, 254 insertions(+), 58 deletions(-) create mode 100644 .github/scripts/tests/test_oci_stack_update_sk.py diff --git a/.github/scripts/tests/test_oci_stack_update_sk.py b/.github/scripts/tests/test_oci_stack_update_sk.py new file mode 100644 index 00000000..4874197e --- /dev/null +++ b/.github/scripts/tests/test_oci_stack_update_sk.py @@ -0,0 +1,196 @@ +"""Coordinated update/recreate contracts; no host lifecycle is permitted.""" +import copy +import json +from pathlib import Path +import subprocess +import sys +from unittest.mock import patch + +from test_oci_update_cancellation import CancellationFixture, ROOT +from proxmenux_oci import cli, i18n, management as menu, ui as native_ui, updates +import oci_instances as instances +import oci_stack_plan as stack_plan +import oci_stack_transaction as transaction +import oci_ui + + +class StackFixture(CancellationFixture): + def setUp(self): + super().setUp() + self.row['stack'] = True + self.record.update(installation_id='fiction-main', stack={ + 'id': 'fiction-main', 'members': [ + {'vmid': vmid, 'installation_id': identity, 'status': 'installed', + 'stack_member': {'stack_id': 'fiction-main', 'primary_vmid': 110}} + for vmid, identity in [(110, 'fiction-main'), (111, 'fiction-dependent')]], + 'deployment': {'services': [{'vmid': 111}, {'vmid': 110}]}}) + self.original = copy.deepcopy(self.record) + self.extra_effects = [self.guard(Path, name) for name in ('write_text', 'write_bytes', 'unlink')] + self.extra_effects += [self.guard(subprocess, name) for name in ('run', 'Popen', 'check_output')] + self.extra_effects += [self.guard(transaction, 'execute'), self.guard(transaction, 'save'), + self.guard(menu.images, 'offer_removal')] + + def no_effects(self): + super().no_effects() + for effect in self.extra_effects: + effect.assert_not_called() + + +class StackCancellationTests(StackFixture): + def test_update_and_recreate_no_are_falsy_distinct_cancellation(self): + for action in ('update', 'recreate'): + with self.subTest(action=action): + result = menu.manage_instance(ROOT/'oci', self.ui, self.row, action=action) + self.assertIs(result, menu.CANCELLED) + self.assertFalse(result) + self.no_effects() + + def test_real_cli_cancel_returns_six_for_both_actions(self): + for action in ('update', 'recreate'): + with self.subTest(action=action), patch.object(cli, 'Catalog'): + self.assertEqual(cli.main(['manage', '110', '--action', action]), 6) + self.no_effects() + + def test_actual_dialog_no_and_escape_return_six_before_any_effect(self): + dialog = native_ui.DialogUI() + for action in ('update', 'recreate'): + for code in (1, 255): + with self.subTest(action=action, code=code), \ + patch.object(native_ui, 'interactive_ui', return_value=dialog), \ + patch.object(dialog, '_run', return_value=subprocess.CompletedProcess('dialog', code, stderr='')): + self.assertEqual(menu.direct_management(ROOT/'oci', 110, action), 6) + self.no_effects() + + def test_confirmation_defaults_and_member_count_are_preserved(self): + for action, default in [('update', True), ('recreate', False)]: + menu.manage_instance(ROOT/'oci', self.ui, self.row, action=action) + args, kwargs = self.ui.review.call_args + self.assertIn('2 containers', args[0]) + self.assertIn('main CT: 110', args[0]) + self.assertEqual(kwargs['default'], default) + self.no_effects() + + def test_native_menu_returns_to_the_application_list_after_no(self): + for action in ('update', 'recreate'): + with self.subTest(action=action): + self.ui.choose.reset_mock() + self.ui.choose.side_effect = ['110', action, None] + with patch.object(menu, '_clean_orphans'), patch.object(menu, 'offer_recovery'), \ + patch.object(menu, '_records_unread', return_value=[]): + menu.interactive_management(ROOT/'oci', self.ui) + self.assertEqual(self.ui.choose.call_count, 3) + self.no_effects() + + def test_batch_cancel_never_checks_the_next_application(self): + available = [{'vmid': 110, 'title': 'First'}, {'vmid': 112, 'title': 'Second'}] + self.ui.checklist.return_value = ['all'] + self.ui.review.side_effect = [True, False] + with patch.object(updates, 'UpdateCheck') as checker, \ + patch.object(menu, 'saved_inventory', return_value=[self.row, {**self.row, 'vmid': 112}]), \ + patch.object(menu, 'check_selected', side_effect=lambda project, row: row) as selected: + checker.return_value.status.return_value = {'available': available} + self.assertFalse(updates.select_updates(ROOT/'oci', self.ui, available)) + self.assertEqual([call.args[1]['vmid'] for call in selected.call_args_list], [110]) + self.no_effects() + + def test_rejected_backup_remains_failure_one_without_dispatch(self): + self.ui.review.return_value = True + self.backup.side_effect = None + self.backup.return_value = False + for action in ('update', 'recreate'): + self.assertEqual(menu.direct_management(ROOT/'oci', 110, action), 1) + self.lifecycle.assert_not_called() + self.assertEqual(self.record, self.original) + + def test_positive_confirmation_preserves_success_failure_and_exact_commands(self): + self.ui.review.return_value = True + self.backup.side_effect = None + self.backup.return_value = True + self.lifecycle.side_effect = None + for action in ('update', 'recreate'): + for result in (False, True): + self.lifecycle.reset_mock() + self.lifecycle.return_value = result + with patch.object(menu.images, 'offer_removal') as offer: + self.assertEqual(menu.direct_management(ROOT/'oci', 110, action, + lifecycle_args=('--keep-backup',)), 0 if result else 1) + expected = [sys.executable, str(ROOT/'oci/remote/oci_stack_native.py'), '110', '--keep-backup'] + if action == 'recreate': + expected += ['--operation', 'recreate'] + self.assertEqual(self.lifecycle.call_args.args[0], expected+['--acknowledge-external-data']) + self.assertEqual(offer.call_count, int(result)) + self.assertEqual(self.record, self.original) + self.write.assert_not_called() + self.command.assert_not_called() + + def test_acknowledgement_is_not_duplicated(self): + self.ui.review.return_value = True + self.backup.side_effect = None + self.backup.return_value = True + self.lifecycle.side_effect = None + self.lifecycle.return_value = False + menu.direct_management(ROOT/'oci', 110, 'recreate', lifecycle_args=('--acknowledge-external-data',)) + self.assertEqual(self.lifecycle.call_args.args[0].count('--acknowledge-external-data'), 1) + + +class StackSlovakTests(StackFixture): + def setUp(self): + super().setUp() + self.catalog = json.loads((ROOT/'lang/sk.json').read_text()) + for module in (i18n, oci_ui): + for name, value in [('_language', 'sk'), ('_cache', self.catalog)]: + item = patch.object(module, name, value) + item.start() + self.addCleanup(item.stop) + + def test_questions_and_titles_name_the_whole_application(self): + for action, question in [('update', 'Aktualizovať celú aplikáciu?'), + ('recreate', 'Znovu vytvoriť všetky kontajnery aplikácie?')]: + menu.manage_instance(ROOT/'oci', self.ui, self.row, action=action) + self.assertEqual(self.ui.review.call_args.kwargs['question'], question) + self.assertIn('aplikáciu s viacerými kontajnermi', self.ui.review.call_args.args[1]) + self.no_effects() + + def test_recreation_dialog_explains_saved_images_and_restoring_backups(self): + menu.manage_instance(ROOT/'oci', self.ui, self.row, action='recreate') + text = self.ui.review.call_args.args[0] + self.assertIn('uložených identifikátorov obrazov', text) + self.assertIn('2', text) + self.assertIn('110', text) + self.assertIn('zálohy sa obnovia', text) + self.assertNotIn('Stack', text) + self.no_effects() + + def test_shared_missing_primary_error_in_plan_and_publish_before_any_effect(self): + primary = copy.deepcopy(self.record) + primary['vmid'] = 112 + with self.assertRaisesRegex(ValueError, '^Chýba hlavný kontajner aplikácie$'): + stack_plan.build(primary, {}, {}, 'update') + with self.assertRaisesRegex(ValueError, '^Chýba hlavný kontajner aplikácie$'): + instances.publish_stack(Path('/fiction-only'), 110, {}, {}, []) + self.no_effects() + + def test_missing_member_error_keeps_volume_verification_requirement(self): + records = {row['vmid']: row for row in self.record['stack']['members']} + with self.assertRaisesRegex(ValueError, 'znova ich vytvorte až po overení ich dátových zväzkov'): + stack_plan.build(self.record, records, {110: 'fiction-main'}, 'update') + self.no_effects() + + def test_pending_and_conflicting_member_errors_keep_the_identifier(self): + records = {row['vmid']: copy.deepcopy(row) for row in self.record['stack']['members']} + inventory = {key: row['installation_id'] for key, row in records.items()} + records[111]['status'] = 'pending' + with self.assertRaisesRegex(ValueError, '^Nedokončená operácia existuje pre CT 111$'): + stack_plan.build(self.record, records, inventory, 'update') + records[111]['status'] = 'installed' + inventory[111] = 'different-installation' + with self.assertRaisesRegex(ValueError, '^Iná inštancia už používa VMID 111$'): + stack_plan.build(self.record, records, inventory, 'update') + self.no_effects() + + def test_plan_failure_is_read_only_and_distinguishes_invalid_identity(self): + primary = copy.deepcopy(self.record) + primary['stack']['id'] = 'different-stack' + with self.assertRaisesRegex(ValueError, '^Nekonzistentná identita aplikácie s viacerými kontajnermi$'): + stack_plan.build(primary, {}, {}, 'recreate') + self.no_effects() diff --git a/lang/sk.json b/lang/sk.json index d395a92a..facb91e2 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -68,21 +68,21 @@ "A host reboot is required before starting the VM. Reboot now?": "Pred spustením VM je potrebný reštart servera. Reštartovať teraz?", "A host script is not written over a symbolic link:": "Hostiteľský skript sa nepíše cez symbolický odkaz:", "A job with this ID already exists.": "Úloha s týmto ID už existuje.", - "A journal already exists; review or recover it before trying again": "Časopis už existuje; recenzia alebo obnoviť pred pokusom opäť", + "A journal already exists; review or recover it before trying again": "Záznam operácie už existuje; pred ďalším pokusom ho skontrolujte alebo vykonajte obnovu", "A keyfile is installed at:": "Keyfile je nainštalovaný v:", "A legacy gasket-dkms package was found on this host, but no Coral M.2 / PCIe hardware is present.": "Na tomto hostiteľovi sa našiel starý balík gasket-dkms, ale nie je prítomný žiadny hardvér Coral M.2 / PCIe.", "A managed rootfs and an unprivileged container are required": "Riadené rootfs a neprivilegovaný kontajner sú required", "A managed volume with backup enabled is required": "Riadený objem so zapnutou zálohou je required", "A matching managed host firewall rule may also be removed.": "Môže sa odstrániť aj zodpovedajúce spravované pravidlo firewallu hostiteľa.", "A member VMID is in use by another guest or is on another node": "VMID člena aplikácie používa iný hosť alebo je kontajner na inom uzle", - "A member configuration changed after the stack was checked": "Konfigurácia člena sa zmenila po kontrole zásobníka", + "A member configuration changed after the stack was checked": "Nastavenie kontajnera sa po kontrole aplikácie zmenilo", "A member configuration changed during the preparation": "Konfigurácia člena sa počas prípravy zmenila", - "A member did not pass its service check:": "Člen neprešiel servisnou kontrolou:", - "A member has a pending operation": "Člen čaká na operation", - "A member has no reproducible service check": "Člen nemá reprodukovateľnú servisnú kontrolu", + "A member did not pass its service check:": "Kontajner neprešiel kontrolou služby:", + "A member has a pending operation": "Jeden z kontajnerov má nedokončenú operáciu", + "A member has no reproducible service check": "Jeden z kontajnerov nemá kontrolu služby, ktorú možno zopakovať", "A member is missing before the replacement": "Pred nahradením chýba kontajner aplikácie", - "A member operation does not belong to the stack": "Člen operation nepatrí do stohu", - "A member stopped:": "Člen zastavil:", + "A member operation does not belong to the stack": "Operácia kontajnera nepatrí do tejto aplikácie", + "A member stopped:": "Kontajner bol zastavený:", "A member was modified after it was recovered": "Člen bol zmenený po tom, čo bol obnovený", "A modern wiki and knowledge base for teams": "Moderná wiki a vedomostná základňa pre tímy", "A multi-container application is not recreated: its containers are updated together.": "Aplikácia s viacerými kontajnermi sa nevytvorí znova: jej kontajnery sa aktualizujú spoločne.", @@ -90,7 +90,7 @@ "A new kernel is staged for the next boot:": "Na najbližší štart je pripravený nový kernel:", "A newer kernel is installed. Reboot before the next backup window to use it.": "Je nainštalované novšie jadro. Aby ste ho mohli použiť, reštartujte ho pred ďalším oknom zálohy.", "A newer version is available:": "Je dostupná novšia verzia:", - "A pending operation exists for": "Čaká na operation existuje", + "A pending operation exists for": "Nedokončená operácia existuje pre", "A pending stack assembly already exists; it is not overwritten": "Nevyriešená zostava stohov už existuje; nie je prepísaná", "A previous NVIDIA refresh is pending review": "Predchádzajúce obnovovanie NVIDIA čaká na preskúmanie", "A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "Pre nasledujúce GPU NVIDIA bola zistená predchádzajúca konfigurácia priechodu VFIO:", @@ -364,7 +364,7 @@ "All {count} containers of the application are updated together (main CT: {vmid}). If there are new versions, all images are downloaded and verified, the application is stopped, each container is backed up and replaced with its new image. If anything fails, the backups are restored.": "Všetky kontajnery {count} aplikácie sa aktualizujú spoločne (hlavný CT: {vmid}).Ak existujú nové verzie, všetky obrázky sa stiahnu a overia, aplikácia sa zastaví, každý kontajner sa zálohuje a nahradí sa novým obrázkom. Ak niečo zlyhá, zálohy sa obnovia.", "All {count} containers of the application are updated together (main CT: {vmid}). If there are new versions, all images are downloaded and verified, the application is stopped, each container is backed up and replaced with its new image. If the operation fails after a container was changed, the backups are restored. An operation that stops halfway is recovered from the OCI management menu.": "Všetkých {count} kontajnerov aplikácie sa aktualizuje spoločne (hlavný CT: {vmid}). Ak existujú nové verzie, všetky obrazy sa stiahnu a overia, aplikácia sa zastaví, každý kontajner sa zálohuje a nahradí sa novým obrazom. Ak operácia zlyhá po zmene kontajnera, zálohy sa obnovia. Operácia, ktorá sa zastaví v polovici, sa obnoví z ponuky správy OCI.", "All {count} containers of the application will be recreated from their saved image digests (main CT: {vmid}). The stack is stopped, every container is backed up and replaced, then checked. If anything fails, the backups are restored.": "Všetkých {count} kontajnerov aplikácie sa znovu vytvorí z uložených image digestov (hlavný CT: {vmid}). Stack sa zastaví, každý kontajner sa zazálohuje, nahradí a skontroluje. Ak niečo zlyhá, zálohy sa obnovia.", - "All {count} containers of the application will be recreated from their saved image digests (main CT: {vmid}). The stack is stopped, every container is backed up and replaced, then checked. If the operation fails after a container was changed, the backups are restored. An operation that stops halfway is recovered from the OCI management menu.": "Všetkých {count} kontajnerov aplikácie sa znovu vytvorí z uložených image digestov (hlavný CT: {vmid}). Stack sa zastaví, každý kontajner sa zazálohuje, nahradí a skontroluje. Ak operácia zlyhá po zmene kontajnera, zálohy sa obnovia. Operácia, ktorá sa zastaví v polovici, sa obnoví z ponuky správy OCI.", + "All {count} containers of the application will be recreated from their saved image digests (main CT: {vmid}). The stack is stopped, every container is backed up and replaced, then checked. If the operation fails after a container was changed, the backups are restored. An operation that stops halfway is recovered from the OCI management menu.": "Všetky kontajnery aplikácie ({count}, hlavný CT: {vmid}) sa znovu vytvoria z uložených identifikátorov obrazov (digestov). Aplikácia sa zastaví, každý kontajner sa zazálohuje, nahradí a skontroluje. Ak operácia zlyhá po zmene kontajnera, zálohy sa obnovia. Nedokončenú operáciu možno obnoviť z ponuky správy OCI.", "Allocate CPU Cores": "Prideliť CPU jadrá", "Allocate RAM in MiB": "Prideliť RAM v MiB", "Allow TCP port {port} from {subnet} through the host firewall? Existing firewall rules are not changed.": "Povoliť cez firewall hostiteľa TCP port {port} zo siete {subnet}? Existujúce pravidlá firewallu sa nemenia.", @@ -415,7 +415,7 @@ "Another ZRAM generator configuration exists; refusing to overwrite it:": "Existuje iná konfigurácia generátora ZRAM; refusing to overwrite it:", "Another ZRAM swap setup exists; refusing to replace it:": "Existuje ďalšie nastavenie výmeny ZRAM; odmietnutie jeho výmeny:", "Another device of the network already answers on this address:": "Na tejto adrese už odpovedá iné zariadenie v sieti:", - "Another instance uses": "Iný prípad použitia", + "Another instance uses": "Iná inštancia už používa", "Another job that writes to the same storage still starts them.": "Iná úloha, ktorá zapisuje do rovnakého úložiska, ich spúšťa naďalej.", "Another node, typed by hand": "Ďalší uzol, zadaný ručne", "Another registry account operation is running. Try again when it finishes.": "Prebieha iná operácia registrového účtu. Skúste to znova, keď to skončí.", @@ -860,14 +860,14 @@ "Checking the image in the registry...": "Kontrola obrazu v registri...", "Checking the interrupted operation...": "Kontrolujem prerušenú operáciu...", "Checking the interrupted stack operation...": "Kontrolujem prerušenú operáciu celej aplikácie...", - "Checking the new image without starting it:": "Kontrola nového obrázku bez jeho spustenia:", - "Checking the recreated stack...": "Kontroluje sa znovu vytvorený stack...", + "Checking the new image without starting it:": "Kontrolujem nový obraz bez jeho spustenia:", + "Checking the recreated stack...": "Kontrolujem znovu vytvorené kontajnery aplikácie...", "Checking the remote...": "Kontrolujem ovládač...", "Checking the restored installation": "Kontrola obnovenej inštalácie", "Checking the restored installation...": "Kontrola obnovenej inštalácie...", - "Checking the stack before recreating...": "Kontrolujem stack pred opätovným vytvorením...", - "Checking the stack before the update...": "Kontrola zásobníka pred aktualizáciou...", - "Checking the updated stack...": "Kontrolujem aktualizovaný stack...", + "Checking the stack before recreating...": "Kontrolujem aplikáciu pred opätovným vytvorením kontajnerov...", + "Checking the stack before the update...": "Kontrolujem aplikáciu pred aktualizáciou...", + "Checking the updated stack...": "Kontrolujem aktualizovanú aplikáciu...", "Checklist post-upgrade finished. Warnings:": "Kontrola po aktualizácii dokončená. Varovania:", "Checklist pre-check finished. Warnings:": "Predbežná kontrola dokončená. Varovania:", "Checks for LVM and storage issues": "Skontroluje problémy s LVM a úložiskami", @@ -1009,7 +1009,7 @@ "Completed. Disks added to CT {ctid}: {count}.": "Dokončené. Disky pridané do CT {ctid}: {count}.", "Completed. Disks added to VM {vmid}: {count}.": "Dokončené. Disky pridané do VM {vmid}: {count}.", "Completed. Press Enter to return to menu...": "Dokončené. Stlačte Enter pre návrat do menu...", - "Completing its final cleanup...": "Dokončiť záverečné čistenie...", + "Completing its final cleanup...": "Dokončujem záverečné upratovanie...", "Completing pending package configurations...": "Dokončenie čakajúcich konfigurácií balíka...", "Compliance checking (PCI-DSS, HIPAA, etc.)": "kontroly súladu (PCI-DSS, HIPAA atď.)", "Component to uninstall manually (no --auto-uninstall yet):": "Súčasť na ručné odinštalovanie (zatiaľ bez --auto-uninstall):", @@ -1026,7 +1026,7 @@ "Concise output of logical volumes": "Stručný výpis logických zväzkov", "Concise output of physical volumes": "Stručný výpis fyzických zväzkov", "Concise output of volume groups": "Stručný výpis skupín zväzkov", - "Concurrent change while restoring the start at boot setting": "Súčasná zmena pri obnovení štartu pri štarte", + "Concurrent change while restoring the start at boot setting": "Pri obnove nastavenia automatického spustenia sa zistila súbežná zmena", "Configuration Analysis": "Analýza nastavenia", "Configuration Menu": "Nastavenia", "Configuration Summary:": "Zhrnutie nastavenia:", @@ -1131,7 +1131,7 @@ "Confirm export": "Potvrdiť export", "Confirm password for": "Potvrďte heslo pre", "Confirm recovery passphrase:": "Potvrďte obnovovaciu frázu:", - "Confirm that host data is not reverted": "Potvrďte, že údaje hostiteľa nie sú vrátené", + "Confirm that host data is not reverted": "Potvrďte, že údaje v adresároch hostiteľa sa pri obnove nevracajú do predchádzajúceho stavu", "Confirm the keyfile passphrase:": "Potvrďte frázu keyfile:", "Confirm the mount path is visible.": "Overte, že cesta pripojenia je viditeľná.", "Confirm the password:": "Potvrďte heslo:", @@ -1937,7 +1937,7 @@ "Duplicated VMID in the Proxmox inventory": "Duplikované VMID v inventári Proxmox", "Duplicated native directive:": "Duplikát natívne smernice:", "Duplicated native option": "Duplikované natívne možnosti", - "Duplicated or invalid stack VMID": "Duplikované alebo neplatné stoh VMID", + "Duplicated or invalid stack VMID": "Duplicitné alebo neplatné VMID kontajnerov aplikácie", "Duplicati is a backup client that securely stores encrypted, incremental, compressed backups on local storage, cloud storage services and remote file servers. It works with standard protocols like FTP, SSH, WebDAV as well as popular services like Microsoft OneDrive, Amazon S3, Google Drive, box.com, Mega, B2, and many others.": "Duplicati je záložný klient, ktorý bezpečne ukladá šifrované, prírastkové, stlačené zálohy na lokálne úložisko, cloudové úložné služby a vzdialené súborové servery. Funguje so štandardnými protokolmi ako FTP, SSH, WebDAV, rovnako ako populárne služby ako Microsoft OneDrive, Amazon S3, Google Drive, box.com, Mega, B2, a mnoho ďalších.", "Duplicati web interface (password only)": "Duplicati webové rozhranie (iba heslo)", "Duration": "Trvanie", @@ -2729,7 +2729,7 @@ "High Availability services have been enabled successfully": "Služby High Availability boli úspešne zapnuté", "High Availability setup completed": "Nastavenie High Availability je dokončené", "High availability resources are not supported by this profile": "Tento profil nepodporuje vysoko dostupné zdroje", - "High availability resources are not supported for stacks": "Vysoká dostupnosť zdrojov nie sú podporované pre stohy", + "High availability resources are not supported for stacks": "Aplikácie s viacerými kontajnermi nepodporujú prostriedky spravované službou vysokej dostupnosti (HA)", "High risk confirmation": "Potvrdenie vysokého rizika", "High-Risk GPU Power State": "Rizikový stav napájania GPU", "Home Assistant": "Home Assistant", @@ -3029,8 +3029,8 @@ "Incomplete stack removed": "Neúplný zásobník odstránený", "Inconsistent adaptation profile and recipe": "Nekonzistentný adaptačný profil a recept", "Inconsistent host monitor profile": "Profil nekonzistentného monitora hostiteľa", - "Inconsistent stack identity": "Nejednotná identita stohu", - "Inconsistent stack membership for": "Nekonzistentný stack členstvo pre", + "Inconsistent stack identity": "Nekonzistentná identita aplikácie s viacerými kontajnermi", + "Inconsistent stack membership for": "Nekonzistentné členstvo kontajnera v aplikácii:", "Increase container RAM temporarily to": "Dočasne zvýšte RAM kontajnera na", "Increase file and process limits for advanced workloads": "Zvýšiť limity súborov a procesov pre náročnejšie použitie", "Increase various system limits": "Zvýšiť systémové limity", @@ -3334,14 +3334,14 @@ "Invalid resource value": "Neplatná hodnota prostriedkov", "Invalid resources": "Neplatné zdroje", "Invalid restored volume path": "Neplatná cesta obnoveného objemu", - "Invalid running state": "Neplatný jazdný stav", + "Invalid running state": "Neplatný stav spustenia", "Invalid saved image digest": "Uložený image digest nie je platný", "Invalid security.unprivileged value:": "Neplatné zabezpečenie.neprivilegovaná hodnota:", "Invalid selection": "Neplatný výber", "Invalid service alias": "Neplatný alias služby", "Invalid service check URL": "Neplatné URL služby", "Invalid service check arguments": "Neplatné kontrolné argumenty služby", - "Invalid service check timeout": "Neplatný čas odhlásenia služby", + "Invalid service check timeout": "Neplatný časový limit kontroly služby", "Invalid shared path": "Neplatná spoločná cesta", "Invalid shutdown timeout": "Neplatný čas vypnutia", "Invalid size. Please enter a number in MB (e.g., 128, 256, 512).": "Neplatná veľkosť. Zadajte číslo v MB (napr. 128, 256, 512).", @@ -3652,7 +3652,7 @@ "Makefile missing in": "Makefile chýba v", "Malformed repository entries cleaned": "Poškodené položky repozitárov vyčistené", "Manage OCI": "Správa OCI", - "Manage OCI stack": "Spravovať stack OCI", + "Manage OCI stack": "Spravovať aplikáciu s viacerými kontajnermi", "Manage PBS encryption keyfile": "Spravovať šifrovací keyfile pre PBS", "Manage Secure Gateway": "Spravovať Secure Gateway", "Manage and inspect VM disk images": "Spravovať a kontrolovať diskové obrazy VM", @@ -4048,7 +4048,7 @@ "New Virtual Machine": "Nová virtuálna mašina", "New backup job": "Nová úloha zálohy", "New backups on this host will be unencrypted until a new keyfile is set up.": "Nové zálohy na tomto hostovi budú nešifrované, kým nenastavíte nový keyfile.", - "New image compatible:": "Nový obrázok kompatibilný:", + "New image compatible:": "Nový obraz je kompatibilný:", "New image installed": "Inštalovaný nový obrázok", "New image installed, not verified yet": "Nový obraz je nainštalovaný, zatiaľ neoverený", "New image verified, not saved yet": "Nový obraz je overený, zatiaľ neuložený", @@ -5085,13 +5085,13 @@ "Recovery setup failed": "Nastavenie obnovy zlyhalo", "Recreate": "Obnoviť", "Recreate OCI": "Obnoviť OCI", - "Recreate OCI stack": "Znovu vytvoriť OCI stack", + "Recreate OCI stack": "Znovu vytvoriť aplikáciu s viacerými kontajnermi", "Recreate every container with its saved configuration": "Znovu vytvoriť každý kontajner s uloženými nastaveniami", - "Recreate the whole stack?": "Znovu vytvoriť celý stack?", + "Recreate the whole stack?": "Znovu vytvoriť všetky kontajnery aplikácie?", "Recreate with these options?": "Obnoviť tieto možnosti?", "Recreate: add or remove extra paths and devices": "Znovu vytvoriť: pridajte alebo odstráňte ďalšie cesty a zariadenia", "Recreate: edit resources, network, paths and GPU": "Obnoviť: editovať zdroje, sieť, cesty a GPU", - "Recreated stack checked": "Skontrolovaný znova vytvorený stack", + "Recreated stack checked": "Znovu vytvorené kontajnery aplikácie sú skontrolované", "Recreated:": "Znovu vytvorené:", "Recreating": "Znovu vytváram", "Recreating requires a confirmed proposal": "Obnovenie requires potvrdený návrh", @@ -5347,7 +5347,7 @@ "Restoring the original rpcbind service state...": "Obnovenie pôvodného stavu služby rpcbind...", "Restoring the previous Rclone configuration...": "Obnovenie predchádzajúcej konfigurácie Rclone...", "Restoring the previous backup...": "Obnovenie predchádzajúcej zálohy...", - "Restoring the previous state of the stack...": "Obnovenie predchádzajúceho stavu stohu...", + "Restoring the previous state of the stack...": "Obnovujem predchádzajúci stav celej aplikácie...", "Restoring the stack records...": "Obnovenie záznamov...", "Results will be saved automatically to:": "Výsledky sa automaticky uložia do:", "Results will be saved to:": "Výsledky sa uložia do:", @@ -5381,7 +5381,7 @@ "Root SSH keys/config": "SSH kľúče/nastavenie roota", "Root inside container = root on host system": "Root v kontajneri = root na hostiteľskom systéme", "Root privileges are required": "Vyžadujú sa oprávnenia používateľa root", - "Root privileges on the Proxmox node are required": "Koreňové práva na Proxmox uzla sú required", + "Root privileges on the Proxmox node are required": "Na uzle Proxmox sú potrebné oprávnenia používateľa root", "Root shell/profile config": "Nastavenie shellu/profilu roota", "Root user on the PVE host (default 'root'):": "Root používateľ na PVE hostovi (predvolené 'root'):", "Rootfs size in GB": "Rootfs veľkosť v GB", @@ -5526,7 +5526,7 @@ "Saving the new configuration of the application...": "Ukladá sa nová konfigurácia aplikácie...", "Saving the new configuration...": "Ukladanie novej konfigurácie...", "Saving the record of the application...": "Ukladá sa záznam aplikácie...", - "Saving the stack records...": "Ukladám zásobníky...", + "Saving the stack records...": "Ukladám záznamy kontajnerov aplikácie...", "Scan storage for new content": "Vyhľadať nový obsah v úložisku", "Scanning available physical disks...": "Hľadám dostupné fyzické disky...", "Scanning network for NFS servers...": "Prehľadávam sieť a hľadám NFS servery...", @@ -5979,18 +5979,18 @@ "Stack": "Stack", "Stack adapter not recognized by the translator": "Stack adaptér nerozpoznal prekladateľ", "Stack backups are missing; a partial restore is not allowed": "Chýbajú zálohy, čiastočná obnova nie je povolená.", - "Stack checked:": "Stack skontrolovaný:", - "Stack members are missing; recreate them after verifying their volumes": "Chýbajú členovia Stack; znovu ich vytvoriť po overení ich objemov", + "Stack checked:": "Aplikácia je skontrolovaná:", + "Stack members are missing; recreate them after verifying their volumes": "Chýbajú kontajnery aplikácie; znova ich vytvorte až po overení ich dátových zväzkov", "Stack members are updated together with their stack": "Členovia Stack sú aktualizované spolu s ich stack", "Stack name": "Názov zásobníka", "Stack records restored": "Záznamy všetkých kontajnerov aplikácie sú obnovené", - "Stack records saved": "Uložené Stack záznamy", + "Stack records saved": "Záznamy kontajnerov aplikácie sú uložené", "Stack records saved; no container was reinstalled.": "Stack záznamy uložené; žiadny kontajner bol znovu nainštalovaný.", "Stack recovery completed; every member is back to its previous installation.": "Obnova celej aplikácie je dokončená; všetky jej kontajnery sa vrátili k predchádzajúcej inštalácii.", - "Stack recreation completed. Data kept.": "Opätovné vytvorenie stacku je hotové. Dáta zostali zachované.", + "Stack recreation completed. Data kept.": "Opätovné vytvorenie kontajnerov aplikácie je dokončené. Údaje zostali zachované.", "Stack startup hook installed": "Nainštalovaný štartovací hák Stack", "Stack stopped": "Kontajnery aplikácie sú zastavené", - "Stack update completed. Data kept.": "Aktualizácia Stack dokončená. Údaje uchovávané.", + "Stack update completed. Data kept.": "Aktualizácia aplikácie je dokončená. Údaje zostali zachované.", "Stack:": "Stack:", "Staging directory:": "Prípravný priečinok:", "Staging ready.": "Prípravný priečinok je pripravený.", @@ -6305,7 +6305,7 @@ "The ProxMenux Monitor update checker is not available. Start or update ProxMenux Monitor and try again.": "Kontrola aktualizácií v ProxMenux Monitor nie je dostupná. Spustite alebo aktualizujte ProxMenux Monitor a skúste to znova.", "The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "Aktualizácia Proxmox VE 8 sa nedokončila. Prechod na Proxmox VE 9 sa nespustil.", "The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Chýba zväzok kľúčov archívu Proxmox;Inštalácia Ceph nemôže bezpečne pokračovať", - "The Proxmox inventory and the local configurations differ": "Súpis Proxmox a miestne konfigurácie sa líšia", + "The Proxmox inventory and the local configurations differ": "Inventár Proxmox nezodpovedá miestnym nastaveniam kontajnerov", "The ROCm image has no support for the AMD GPU of this host:": "Obraz ROCm nemá podporu pre AMD GPU tohto hostiteľa:", "The ROCm image of this application has no support for the AMD GPU of this host:": "Obraz ROCm tejto aplikácie nemá podporu pre AMD GPU tohto hostiteľa:", "The Rclone configuration needs at least 1 GB": "Konfigurácia Rklonu potrebuje aspoň 1 GB", @@ -6388,7 +6388,7 @@ "The configuration changed during the NVIDIA refresh": "Konfigurácia sa zmenila počas obnovovania NVIDIA", "The configuration evidence does not match": "Konfiguračný dôkaz nezodpovedá", "The configuration must run as root on Proxmox VE": "Konfigurácia musí bežať ako koreň na Proxmox VE", - "The configuration of a new member changed after it was created": "Nastavenie nového člena sa zmenilo po jeho vytvorení", + "The configuration of a new member changed after it was created": "Nastavenie nového kontajnera sa po jeho vytvorení zmenilo", "The configuration stopped because of an unexpected error": "Konfigurácia sa zastavila kvôli neočakávanej chybe", "The configured credential helper could not be used. Check its installation and credential store.": "Nakonfigurovaný pomocný program na správu prihlasovacích údajov sa nedá použiť. Skontrolujte jeho inštaláciu a úložisko prihlasovacích údajov.", "The console of the container could not be configured": "Konzolu kontajnera nebolo možné nakonfigurovať", @@ -6441,8 +6441,8 @@ "The data and document volumes need at least 8 GB": "Údaje a objemy dokumentov potrebujú najmenej 8 GB", "The database server must accept connections from the IP address of this container, with a user that is not limited to localhost.": "Databázový server musí prijímať pripojenia z IP adresy tohto kontajnera s užívateľom, ktorý nie je obmedzený na localhost.", "The dedicated adapter still requires replaying its rootfs changes": "Špecializovaný adaptér stále requires prehráva svoje rootfs zmeny", - "The dependency hook and the stack recipe differ": "Závislosť hák a stack recept sa líšia", - "The dependency hook was modified; review it before updating": "Závislosť hák bol upravený, prehodnocovať pred aktualizáciou", + "The dependency hook and the stack recipe differ": "Spúšťací skript závislostí nezodpovedá konfigurácii aplikácie", + "The dependency hook was modified; review it before updating": "Spúšťací skript závislostí bol zmenený; pred aktualizáciou ho skontrolujte", "The developer-friendly cloud platform for building and running LLM agents for AI-native applications.": "Developer-friendly cloud platforma pre budovanie a prevádzku LLM agentov pre AI-natívne aplikácie.", "The device directory does not exist:": "Adresár zariadenia neexistuje:", "The device must keep its /dev path inside the LXC:": "Zariadenie musí udržať svoju / Dev cestu vnútri LXC:", @@ -6567,7 +6567,7 @@ "The machine learning container is rebuilt with the image of the new choice. Its model cache, the library and the database are kept. The whole application is stopped and updated, as in an update; if anything fails, the previous containers and the previous choice are restored.": "Kontajner strojového učenia je prestavaný s obrazom novej voľby. Uchováva sa jej vyrovnávacia pamäť modelu, knižnica a databáza. Celá aplikácia sa zastaví a aktualizuje ako pri aktualizácii; ak čokoľvek zlyhá, obnovia sa predchádzajúce kontajnery a predchádzajúca voľba.", "The main container of this application is not on this host. Restore it too.": "Hlavný kontajner tejto aplikácie nie je na tomto hostiteľovi. Obnovte ho tiež.", "The main member must stop first and start last": "Hlavný kontajner sa musí zastaviť ako prvý a spustiť ako posledný", - "The main member of the stack is missing": "Hlavný člen zásobníka chýba", + "The main member of the stack is missing": "Chýba hlavný kontajner aplikácie", "The managed host firewall rule could not be removed and was left unchanged.": "Spravované pravidlo firewallu hostiteľa sa nepodarilo odstrániť a zostalo bez zmeny.", "The manifest does not match its digest": "Manifest nezodpovedá jeho tráveniu", "The member journal belongs to another stack operation": "Záznam operácie člena aplikácie patrí k inej operácii celej aplikácie", @@ -6586,13 +6586,13 @@ "The new container did not pass validation": "Nová nádoba neprešla validáciou", "The new container is not authorized by the operation journal": "Záznam operácie nepovoľuje tento nový kontajner", "The new image adds a symbolic link in a generated path": "Nový obrázok pridáva symbolické spojenie na generovanej ceste", - "The new image changes the PostgreSQL major version; the data must be migrated before updating": "Nový obrázok mení PostgreSQL hlavnú verziu; údaje musia byť premiestnené pred aktualizáciou", + "The new image changes the PostgreSQL major version; the data must be migrated before updating": "Nový obraz mení hlavnú verziu PostgreSQL; pred aktualizáciou je potrebné migrovať údaje", "The new image could not be installed": "Nový obrázok nemohol byť nainštalovaný", "The new image could not be installed:": "Nový obrázok sa nepodarilo nainštalovať:", "The new image does not fit on the storage where images are downloaded:": "Nový image sa nezmestí na úložisko, kam sa sťahujú image:", "The new image does not keep a required executable": "Nový obrázok neudržuje spustiteľný required", "The new image is more recent than the minimum age set for scheduled updates; it is not installed yet.": "Nový obrázok je novší ako minimálny vek nastavený pre plánované aktualizácie;ešte nie je nainštalovaný.", - "The new image requires additional persistent paths": "Nový obrázok requires ďalšie pretrvávajúce cesty", + "The new image requires additional persistent paths": "Nový obraz vyžaduje ďalšie trvalé dátové adresáre", "The new image requires additional persistent paths; use Modify": "Nový obraz vyžaduje ďalšie trvalé cesty; použite Upraviť", "The new image requires additional persistent paths; use Recreate": "Nový obrázok requires ďalšie pretrvávajúce cesty; používať Recreate", "The new prompt will be used in new terminal sessions.": "Nový príkazový riadok sa použije v nových reláciách terminálu.", @@ -6658,7 +6658,7 @@ "The read-only view does not apply the expected protection": "Pohľad len na čítanie neuplatňuje očakávanú ochranu", "The read-only view was not published": "Zobrazenie nebolo zverejnené", "The read/write view was not published": "Pohľad na čítanie/ zápis nebol zverejnený", - "The recipe requires configuration at startup; its coordinated replay is not available": "Recept requires konfigurácie pri štarte; jeho koordinovaný opakovaný záznam nie je k dispozícii", + "The recipe requires configuration at startup; its coordinated replay is not available": "Konfigurácia vyžaduje úpravy pri spustení, ktoré koordinovaná aktualizácia zatiaľ nevie zopakovať", "The recognition change of Immich did not complete:": "Zmenu CPU/GPU pre rozpoznávanie v Immichu sa nepodarilo dokončiť:", "The recognition of Immich was not changed:": "Spôsob rozpoznávania v Immichu sa nezmenil:", "The record belongs to another container": "Záznam patrí inému kontajneru", @@ -6768,8 +6768,8 @@ "The specified directory does not exist:": "Zadaný priečinok neexistuje:", "The stability period must be shorter than the healthcheck timeout": "Obdobie stability musí byť kratšie ako časový limit pre kontrolu zdravotného stavu.", "The stack contains devices or directives without a translation": "Stock obsahuje zariadenia alebo smernice bez prekladu", - "The stack does not have the expected native hook": "Stack nemá očakávaný materinský hák", - "The stack journal is outside the registry": "Denník je mimo registra.", + "The stack does not have the expected native hook": "Aplikácia nemá očakávaný natívny spúšťací skript", + "The stack journal is outside the registry": "Záznam operácie celej aplikácie je mimo registra", "The stack member has no declared adaptation profile": "Stack člen nemá deklarovaný adaptačný profil", "The stack name only accepts lowercase letters, numbers and hyphens": "Názov stohu prijíma len malé písmená, čísla a pomlčky", "The stack needs member adaptations or a verification of missing volumes": "Aplikácia vyžaduje úpravy svojich kontajnerov alebo overenie chýbajúcich dátových zväzkov", @@ -6891,7 +6891,7 @@ "This is a simple configuration change": "Ide o jednoduchú zmenu nastavenia", "This is an external community script maintained by": "Toto je externý komunitný skript, ktorý spravuje", "This is an external script that creates a macOS VM in Proxmox VE in just a few steps, whether you are using AMD or Intel hardware.": "Toto je externý skript, ktorý v Proxmox VE vytvorí VM s macOS v niekoľkých krokoch. Funguje na AMD aj Intel hardvéri.", - "This is not a coordinated stack": "Toto nie je koordinovaný stack", + "This is not a coordinated stack": "Toto nie je aplikácia so spoločne spravovanými kontajnermi", "This is not a valid image reference:": "Toto nie je platný odkaz na obrázok:", "This is unexpected since credentials were validated.": "Toto je nečakané, pretože prihlasovacie údaje už boli overené.", "This is what ProxMenux understood from the": "To je to, čo ProxMenux pochopil z", @@ -6933,7 +6933,7 @@ "This session is running in the Monitor terminal. Updating from here would restart the Monitor service and cut the connection mid-install, leaving the update in a broken state.": "Táto relácia beží v termináli Monitor. Aktualizácia odtiaľto by reštartovala službu Monitor a prerušila spojenie uprostred inštalácie, čo by mohlo nechať aktualizáciu v poškodenom stave.", "This shows the storage type and disk identifier": "Týmto sa zobrazí typ úložiska a identifikátor disku", "This stack has no saved members to update.": "Pre túto aplikáciu nie je uložený zoznam kontajnerov.", - "This stack needs rootfs adaptations that coordinated updates cannot replay yet.": "Tento zásobník potrebuje úpravy rootfs, ktoré sa zatiaľ nedajú prehrať koordinované aktualizácie.", + "This stack needs rootfs adaptations that coordinated updates cannot replay yet.": "Táto aplikácia vyžaduje úpravy koreňového súborového systému, ktoré koordinovaná aktualizácia zatiaľ nevie zopakovať.", "This stack requires replaying specific rootfs adaptations. Coordinated updates are not yet enabled for it.": "Tento zásobník requires prehráva špecifické korektúry. Koordinované aktualizácie pre ňu zatiaľ nie sú povolené.", "This state has a high probability of VM startup/reset failures.": "Tento stav má vysokú pravdepodobnosť zlyhania štartu alebo resetu VM.", "This state indicates a high risk of passthrough failure due to": "Tento stav znamená vysoké riziko zlyhania passthrough pre", @@ -7127,18 +7127,18 @@ "Unrecognized Immich adapter": "Nerozpoznaný adaptér Immich", "Unrecognized adaptation format": "Nerozpoznaný formát adaptácie", "Unrecognized adaptation recipe": "Nerozpoznaný adaptačný recept", - "Unrecognized dependency order of the stack:": "Nerozpoznané poradie závislosti stohu:", + "Unrecognized dependency order of the stack:": "Nerozpoznané poradie závislostí aplikácie:", "Unrecognized host monitor profile": "Nerozpoznaný profil monitora hostiteľa", "Unrecognized native configuration": "Nerozpoznaná natívna konfigurácia", "Unrecognized qBittorrent configuration format": "Nerozpoznaný formát konfigurácie qBittorrent", "Unrecognized stack adapter or role": "Nerozpoznaný stack adaptér alebo role", - "Unrecognized stack adapter:": "Nerozpoznaný zásobník adaptér:", - "Unrecognized stack structure:": "Nerozpoznaná štruktúra stohu:", + "Unrecognized stack adapter:": "Nerozpoznaný adaptér aplikácie s viacerými kontajnermi:", + "Unrecognized stack structure:": "Nerozpoznaná štruktúra aplikácie s viacerými kontajnermi:", "Unrecognized volume definition": "Nerozpoznaná definícia objemu", "Unresolved variable:": "Nevyriešená premenná:", "Unsafe OCI archive path": "Nebezpečná cesta k archívu OCI", - "Unsafe dependency contract": "Zmluva o neistej závislosti", - "Unsafe dependency hook contract": "Zmluva o nebezpečnom háku", + "Unsafe dependency contract": "Nebezpečné nastavenie závislostí", + "Unsafe dependency hook contract": "Nebezpečné nastavenie spúšťacieho skriptu závislostí", "Unsafe instance directory": "Nebezpečný priečinok inštancie", "Unsafe instance record": "Nebezpečný záznam o udalosti", "Unsafe journal or lock file": "Nebezpečný súbor denníka alebo zámku", @@ -7201,7 +7201,7 @@ "Update Lynis to latest version": "Aktualizovať Lynis na najnovšiu verziu", "Update NVIDIA in LXC Containers": "Aktualizovať NVIDIA v LXC kontajneroch", "Update OCI": "Aktualizovať aplikáciu", - "Update OCI stack": "Aktualizovať OCI stack", + "Update OCI stack": "Aktualizovať aplikáciu s viacerými kontajnermi", "Update PBS using configured repositories": "Aktualizovať PBS pomocou nakonfigurovaných repozitárov", "Update PVE enterprise repository (Only if using enterprise):": "Aktualizujte PVE enterprise repozitár (iba ak používate enterprise):", "Update Proxmox VE Appliance Manager": "Aktualizovať správcu aplikácií Proxmox VE", @@ -7222,10 +7222,10 @@ "Update selected OCI applications": "Aktualizovať vybrané aplikácie OCI", "Update system to latest PVE 8.4+ (if not done already):": "Aktualizujte systém na najnovšie PVE 8.4+ (ak to ešte nie je hotové):", "Update the image with the saved configuration": "Aktualizovať obrázok pomocou uloženej konfigurácie", - "Update the whole stack?": "Aktualizovať celú kopu?", + "Update the whole stack?": "Aktualizovať celú aplikáciu?", "Updated": "Aktualizované", "Updated sharedfiles group to GID: 101000": "Skupina sharedfiles bola aktualizovaná na GID: 101000", - "Updated stack checked": "Aktualizovaný stack", + "Updated stack checked": "Aktualizovaná aplikácia je skontrolovaná", "Updated:": "Aktualizované:", "Updates all Proxmox and Debian packages": "Aktualizuje všetky balíky Proxmoxu a Debianu", "Updates and Packages Commands": "Príkazy pre aktualizácie a balíky", diff --git a/oci/src/proxmenux_oci/management.py b/oci/src/proxmenux_oci/management.py index 162bdf4f..12b609b3 100644 --- a/oci/src/proxmenux_oci/management.py +++ b/oci/src/proxmenux_oci/management.py @@ -658,7 +658,7 @@ def _manage_stack(project, ui, row, action=None, lifecycle_args=()): 'the OCI management menu.') .format(count=len(members), vmid=primary_id), translate('Recreate OCI stack'), question=translate('Recreate the whole stack?'), default=False): - return False + return CANCELLED else: if not ui.review(translate('All {count} containers of the application are updated together (main CT: {vmid}). ' 'If there are new versions, all images are downloaded and verified, the application ' From 3b12e68e8ce6f6be097844a1d388b6345d9016b8 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 18:00:22 +0000 Subject: [PATCH 19/27] i18n: clarify OCI storage and shared paths in Slovak --- .github/scripts/tests/test_oci_storage_sk.py | 209 +++++++++++++++++++ lang/sk.json | 50 ++--- 2 files changed, 234 insertions(+), 25 deletions(-) create mode 100644 .github/scripts/tests/test_oci_storage_sk.py diff --git a/.github/scripts/tests/test_oci_storage_sk.py b/.github/scripts/tests/test_oci_storage_sk.py new file mode 100644 index 00000000..df1d83bc --- /dev/null +++ b/.github/scripts/tests/test_oci_storage_sk.py @@ -0,0 +1,209 @@ +"""Slovak storage questions through real builders and UI; installation forbidden.""" +from contextlib import redirect_stdout +import copy +import io +import json +from pathlib import Path +import subprocess +import sys +from types import SimpleNamespace +import unittest +from unittest.mock import Mock, patch + +ROOT=Path(__file__).resolve().parents[3] +sys.path[:0]=[str(ROOT/'oci/src'),str(ROOT/'oci/remote')] +from proxmenux_oci import cli, custom, custom_mounts, extra_devices, i18n, installer, ui as native_ui, updates +from proxmenux_oci.catalog import Catalog + + +class Recorder: + def __init__(self, answers=None): + self.answers=answers or {} + self.asked=[] + self.messages=[] + def _answer(self,text,default): + self.asked.append(text) + return self.answers.get(text,default) + def ask(self,text,default=None,**kwargs):return self._answer(text,default if default is not None else '') + def password(self,text,**kwargs):return self._answer(text,'fictional-value-for-tests-000000') + def choose(self,text,options,default=None,**kwargs):return self._answer(text,default) + def confirm(self,text,default=False,**kwargs):return self._answer(text,default) + def checklist(self,text,options,defaults=None,**kwargs):return self._answer(text,defaults or []) + def review(self,*args,**kwargs):return False + def info(self,text):pass + def message(self,text,*args):self.messages.append(text) + + +class StorageFixture(unittest.TestCase): + def setUp(self): + self.catalog=Catalog(ROOT/'oci') + self.sk=json.loads((ROOT/'lang/sk.json').read_text()) + self.forbidden=[] + self.patch(i18n,'_language','sk') + self.patch(i18n,'_cache',self.sk) + self.patch(installer.host,'storages',side_effect=lambda content: [ + {'storage':'local-lvm','type':'lvmthin','avail':100*2**30}, + {'storage':'fiction-storage','type':'dir','avail':200*2**30}] if content=='rootdir' else [ + {'storage':'local','type':'dir','avail':100*2**30}]) + self.patch(installer.host,'bridges',return_value=[{'iface':'vmbr0','cidr':'192.0.2.10/24'}]) + self.patch(installer.host,'timezone',return_value='Europe/Bratislava') + self.patch(installer.host,'gpus',return_value={'intel':[],'amd':[],'nvidia':False}) + self.patch(installer.access,'ask_addresses',side_effect=lambda ui,bridge,names,*a:({name:'dhcp' for name in names},None)) + self.patch(installer.access,'ask_ipv4',return_value=('dhcp',None)) + for module,name in [(installer,'run_remote_install'),(cli,'run_remote_install'), + (cli.images,'offer_removal'),(subprocess,'run'),(subprocess,'Popen'),(subprocess,'check_output'), + (Path,'write_text'),(Path,'write_bytes'),(Path,'unlink')]: + self.forbidden.append(self.patch(module,name,side_effect=AssertionError('Forbidden effect: '+name))) + self.addCleanup(self.no_effects) + def patch(self,module,name,*args,**kwargs): + item=patch.object(module,name,*args,**kwargs);value=item.start();self.addCleanup(item.stop);return value + def no_effects(self): + for effect in self.forbidden:effect.assert_not_called() + def recorder(self,answers=None): + return Recorder({i18n.translate(key):value for key,value in (answers or {}).items()}) + def build(self,app,answers=None,mode=installer.ADVANCED_MODE): + ui=self.recorder(answers) + plan=installer.build_deployment(self.catalog.compose(app),ui,mode) + return ui,plan + + +class StorageWordingTests(StorageFixture): + def test_tandoor_files_question_and_minimum_error_keep_the_limit(self): + ui,plan=self.build('tandoor',{'Files volume size in GB':'9'}) + self.assertIn('Veľkosť dátového zväzku pre súbory v GB',ui.asked) + self.assertEqual(plan['media']['size_gb'],9) + with self.assertRaisesRegex(installer.InstallError,'Dátový zväzok súborov Tandoor musí mať najmenej 2 GB'): + self.build('tandoor',{'Files volume size in GB':'1'}) + + def test_paperless_storage_documents_and_consume_export_names(self): + ui,plan=self.build('paperless-ngx',{'Documents volume size in GB':'30', + 'Where to store the consume and export folders':'managed-volume','Size of each consume/export volume in GB':'3'}) + for text in ['Úložisko pre údaje a dokumenty aplikácie Paperless', + 'Veľkosť dátového zväzku pre dokumenty v GB', + 'Veľkosť každého dátového zväzku pre consume a export v GB']: + self.assertIn(text,ui.asked) + self.assertEqual((plan['media_size_gb'],plan['transfer']['size_gb'],plan['transfer']['backup']),(30,3,True)) + + def test_paperless_volume_limit_and_transfer_limit_errors(self): + with self.assertRaisesRegex(installer.InstallError,'Trvalé dátové zväzky aplikácie Paperless musia mať najmenej 8 GB'): + self.build('paperless-ngx',{'Documents volume size in GB':'7'}) + with self.assertRaisesRegex(installer.InstallError,'Dátové zväzky pre consume a export musia mať najmenej 1 GB'): + self.build('paperless-ngx',{'Where to store the consume and export folders':'managed-volume', + 'Size of each consume/export volume in GB':'0'}) + + def test_postgresql_question_and_error_are_shared_by_immich_and_nextcloud(self): + for app in ('immich','nextcloud-stack'): + with self.subTest(app=app): + ui,plan=self.build(app,{'PostgreSQL volume size in GB':'12'}) + self.assertIn('Veľkosť dátového zväzku PostgreSQL v GB',ui.asked) + self.assertEqual(plan['database_size_gb'],12) + with self.assertRaisesRegex(installer.InstallError,'Dátový zväzok PostgreSQL musí mať najmenej 8 GB'): + self.build(app,{'PostgreSQL volume size in GB':'7'}) + + def test_host_directory_remains_outside_the_container_backup(self): + ui,plan=self.build('nextcloud-stack',{'Where to store the Nextcloud files, configuration and data':'host-bind', + 'Shared host directory':'/fiction-only/existing-data'}) + self.assertIn('Zdieľaný adresár hostiteľa',ui.asked) + self.assertEqual((plan['application']['mode'],plan['application']['host_path'],plan['application']['backup']), + ('host-bind','/fiction-only/existing-data',False)) + + def test_default_and_advanced_builders_preserve_language_independent_plans(self): + for app in ('jellyfin','immich','nextcloud-stack','paperless-ngx','tandoor'): + for mode in (installer.DEFAULT_MODE,installer.ADVANCED_MODE): + with self.subTest(app=app,mode=mode): + _ui,slovak=self.build(app,mode=mode) + with patch.object(i18n,'_language','en'): + _ui,english=self.build(app,mode=mode) + self.assertEqual(slovak,english) + + def test_multi_path_cancel_and_invalid_selection_preserve_all_members(self): + services=[{'name':'main','main':True,'deployment':{'mounts':[]}}, + {'name':'database','deployment':{'mounts':[]}}] + original=copy.deepcopy(services) + ui=self.recorder({'Add extra paths to this stack':True,'Containers that will receive this path':[]}) + with self.assertRaisesRegex(ValueError,'^Vyberte aspoň jeden kontajner aplikácie$'): + custom_mounts.ask_stack_custom_mounts(ui,services,'fiction-storage') + self.assertIn('Pridať ďalšie cesty do aplikácie s viacerými kontajnermi',ui.asked) + self.assertEqual(services,original) + ui=self.recorder({'Add extra paths to this stack':True,'Containers that will receive this path':['main'], + 'Data location':None}) + with self.assertRaises(native_ui.UserCancelled):custom_mounts.ask_stack_custom_mounts(ui,services,'fiction-storage') + self.assertEqual(services,original) + + def test_shared_selection_error_in_device_flow_also_preserves_members(self): + services=[{'name':'main','main':True,'deployment':{}}];original=copy.deepcopy(services) + ui=self.recorder() + with patch.object(extra_devices,'ask_extra_devices',return_value=[{'kind':'usb','host_path':'/fiction-only/device'}]), \ + patch.object(ui,'checklist',return_value=[]): + with self.assertRaisesRegex(ValueError,'^Vyberte aspoň jeden kontajner aplikácie$'): + extra_devices.ask_stack_extra_devices(ui,services) + self.assertEqual(services,original) + + def test_selected_path_is_added_only_to_selected_member_with_backup(self): + services=[{'name':'main','main':True,'deployment':{'mounts':[]}}, + {'name':'database','deployment':{'mounts':[]}}] + ui=self.recorder({'Add extra paths to this stack':True,'Containers that will receive this path':['main']}) + custom_mounts.ask_stack_custom_mounts(ui,services,'fiction-storage') + self.assertEqual(services[1]['deployment']['mounts'],[]) + mount=services[0]['deployment']['mounts'][0] + self.assertEqual((mount['source'],mount['container_path'],mount['size_gb'],mount['backup']), + ('fiction-storage','/media-extra',8,True)) + self.assertIn('Pridať ďalšiu cestu do aplikácie s viacerými kontajnermi',ui.asked) + + def test_rclone_shared_directory_question_is_localized_without_creating_it(self): + ui=self.recorder({'Shared directory for copy/sync operations':'/fiction-only/rclone'}) + with patch.object(ui,'confirm',return_value=True): + plan=installer.build_deployment(self.catalog.compose('rclone'),ui,installer.ADVANCED_MODE) + self.assertIn('Zdieľaný adresár na kopírovanie a synchronizáciu',ui.asked) + self.assertTrue(any(m.get('source')=='/fiction-only/rclone' and not m['backup'] for m in plan['mounts'])) + + +class InputAndCancellationTests(StorageFixture): + def test_dialog_required_value_and_password_use_slovak_then_retry(self): + dialog=native_ui.DialogUI() + empty=subprocess.CompletedProcess('dialog',0,stderr='') + value=subprocess.CompletedProcess('dialog',0,stderr='fiction-value') + with patch.object(dialog,'_run',side_effect=[empty,value]),patch.object(dialog,'message') as message: + self.assertEqual(dialog.ask('fiction-field'),'fiction-value') + message.assert_called_once_with('Táto hodnota je povinná.') + with patch.object(dialog,'_run',side_effect=[empty,value,value]),patch.object(dialog,'message') as message: + self.assertEqual(dialog.password('fiction-field'),'fiction-value') + message.assert_called_once_with('Táto hodnota je povinná.') + + def test_terminal_required_value_and_password_are_also_localized(self): + terminal=native_ui.TerminalUI() + with patch('builtins.input',side_effect=['','fiction-value']),redirect_stdout(io.StringIO()) as output: + self.assertEqual(terminal.ask('fiction-field'),'fiction-value') + self.assertIn('Táto hodnota je povinná.',output.getvalue()) + with patch.object(native_ui.getpass,'getpass',side_effect=['','fiction-value','fiction-value']),redirect_stdout(io.StringIO()) as output: + self.assertEqual(terminal.password('fiction-field'),'fiction-value') + self.assertIn('Táto hodnota je povinná.',output.getvalue()) + + def test_real_install_flow_storage_cancel_and_final_no_never_dispatch(self): + template=self.catalog.compose('tandoor');original=copy.deepcopy(template) + dialog=native_ui.DialogUI() + for code in (1,255): + replies=[subprocess.CompletedProcess('dialog',0,stderr=value) for value in ('','fiction-stack','2','2048')] + replies.append(subprocess.CompletedProcess('dialog',code,stderr='')) + with self.subTest(code=code),patch.object(dialog,'_run',side_effect=replies) as run: + with self.assertRaises(native_ui.UserCancelled):cli.install_template(dialog,template,'tandoor',installer.ADVANCED_MODE) + self.assertEqual(run.call_count,5) + self.assertEqual(template,original) + self.assertIsNone(cli.install_template(self.recorder(),template,'tandoor',installer.ADVANCED_MODE)) + self.assertEqual(template,original) + + def test_numeric_retry_keeps_previous_answers_and_already_correct_slovak(self): + ui=Recorder();wizard=native_ui.BacktrackUI(ui) + wizard.answers=[('ask','previous'),('ask','abc')];wizard.cursor=2 + self.assertTrue(wizard.retry_last(ValueError("invalid literal for int() with base 10: 'abc'"))) + self.assertEqual(ui.messages,["Hodnota musí byť číslo: 'abc'\n\nZadajte hodnotu znova."]) + self.assertEqual((wizard.answers,wizard.cursor),([('ask','previous')],0));wizard.close() + + def test_real_menu_error_handler_has_slovak_heading_and_preserves_detail(self): + ui=Mock();ui.choose.side_effect=['custom',None] + with patch.object(cli,'interactive_ui',return_value=ui),patch.object(custom,'explore',side_effect=ValueError('fiction-detail')), \ + patch.object(updates,'UpdateCheck') as checker: + check=checker.return_value.__enter__.return_value + check.active=False;check.notice.return_value='';check.status.return_value={'available':[]} + self.assertEqual(cli.interactive(self.catalog),0) + ui.message.assert_called_once_with('Operáciu sa nepodarilo dokončiť:\n\nfiction-detail') diff --git a/lang/sk.json b/lang/sk.json index facb91e2..5e6520b8 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -240,7 +240,7 @@ "Add an extra custom path": "Pridať extra vlastnú cestu", "Add another GPU or USB device manually?": "Pridať ďalšie zariadenie GPU alebo USB manuálne?", "Add another directory to share?": "Pridať ďalší adresár na zdieľanie?", - "Add another extra path to this stack": "Pridajte do tohto zásobníka ďalšiu cestu navyše", + "Add another extra path to this stack": "Pridať ďalšiu cestu do aplikácie s viacerými kontajnermi", "Add as IDE": "Pridať ako IDE", "Add as SATA": "Pridať ako SATA", "Add as SCSI": "Pridať ako SCSI", @@ -252,7 +252,7 @@ "Add disk to LXC container": "Pridať disk do LXC kontajnera", "Add explicit privileged flag (optional but recommended):": "Pridať výslovný príznak privilegovaného kontajnera (voliteľné, ale odporúčané):", "Add export rule:": "Pridať export pravidlo:", - "Add extra paths to this stack": "Pridajte do tohto zásobníka ďalšie cesty", + "Add extra paths to this stack": "Pridať ďalšie cesty do aplikácie s viacerými kontajnermi", "Add iSCSI Target as Proxmox Storage": "Pridať iSCSI cieľ ako úložisko Proxmoxu", "Add iSCSI storage in Proxmox Storage": "Pridať iSCSI úložisko v Proxmoxe", "Add iSCSI storage in Proxmox:": "Pridať iSCSI úložisko do Proxmoxu:", @@ -1617,7 +1617,7 @@ "Data location": "Umiestnenie údajov", "Data size:": "Veľkosť dát:", "Data that is deleted with them:": "Údaje, ktoré sa spolu s nimi vypúšťajú:", - "Data volume size in GB": "Veľkosť dátového objemu v GB", + "Data volume size in GB": "Veľkosť dátového zväzku v GB", "Data volumes protected": "Dátové zväzky sú chránené", "Data wipe complete.": "Vymazanie dát je dokončené.", "Data wiped from": "Dáta boli vymazané z", @@ -1635,8 +1635,8 @@ "Debian repositories missing; creating default source file": "Debian repozitáre chýbajú; vytváram predvolený zdrojový súbor", "Decompress backup manually": "Ručne rozbaliť zálohu", "Decryption failed. The passphrase may be wrong, or the blob is corrupt. Try again?": "Dešifrovanie zlyhalo. Fráza môže byť nesprávna alebo je balíček poškodený. Skúsiť znova?", - "Dedicated container volume (included in backups)": "Dedikovaný objem kontajnera (zahrnutý do zálohy)", - "Dedicated container volumes (included in backups)": "Vyhradené objemy kontajnerov (zahrnuté do záloh)", + "Dedicated container volume (included in backups)": "Samostatný dátový zväzok kontajnera (zahrnutý v zálohách)", + "Dedicated container volumes (included in backups)": "Samostatné dátové zväzky kontajnerov (zahrnuté v zálohách)", "DeepSeek Harness “Everything is a Plugin“.": "DeepSeek Harness, všetko je Plugin.", "Default ACLs applied for group inheritance.": "Predvolené ACL boli použité pre dedenie skupiny.", "Default Credentials": "Predvolené prihlasovacie údaje", @@ -1884,7 +1884,7 @@ "Docker Volume Backup": "Docker Volume Backup", "Docker/CLI not available yet or no valid answer": "Docker/CLI ešte nie je k dispozícii alebo nie je platná odpoveď", "Documents & Notes": "Dokumenty a poznámky", - "Documents volume size in GB": "Veľkosť objemu dokumentov v GB", + "Documents volume size in GB": "Veľkosť dátového zväzku pre dokumenty v GB", "Dokuwiki is a simple to use and highly versatile Open Source wiki software that doesn't require a database. It is loved by users for its clean and readable syntax. The ease of maintenance, backup and integration makes it an administrator's favorite. Built in access controls and authentication connectors make DokuWiki especially useful in the enterprise context and the large number of plugins contributed by its vibrant community allow for a broad range of use cases beyond a traditional wiki.": "Dokuwiki je jednoduché použitie a vysoko univerzálny softvér Open Source wiki, ktorý nerequire databázu. Používatelia ho milujú pre jeho čistú a čitateľnú syntax. Jednoduchosť údržby, zálohovania a integrácie z neho robí administrátora obľúbeným. Postavený v prístupových a autentifikačných konektorov, aby DokuWiki obzvlášť užitočné v podnikovom kontexte a veľký počet pluginov, ktoré prispeli jeho pulzujúce komunity umožňujú širokú škálu prípadov použitia mimo tradičné wiki.", "Dolphin Emulator lets you play GameCube and Wii games with various graphical enhancements and other features are available to improve your game experience.": "Dolphin Emulator vám umožní hrať hryCube a Wii hry s rôznymi grafickými vylepšeniami a ďalšie funkcie sú k dispozícii pre zlepšenie herné skúsenosti.", "Domain for the certificate (example.com)": "Doména pre osvedčenie (príklad.com)", @@ -2453,7 +2453,7 @@ "Files & Downloads": "Súbory a sťahovanie", "Files & Sharing": "Súbory a zdieľanie", "Files it writes in the container:": "Súbory, ktoré zapisuje do kontajnera:", - "Files volume size in GB": "Veľkosť hlasitosti súborov v GB", + "Files volume size in GB": "Veľkosť dátového zväzku pre súbory v GB", "Filesystem": "Súborový systém", "Filesystem Tools Required": "Sú potrebné nástroje pre súborový systém", "Filesystem:": "Súborový systém:", @@ -4063,7 +4063,7 @@ "Next step: stop that VM first, then run": "Ďalší krok: najprv zastavte tú VM, potom spustite", "Nextcloud configuration cancelled": "Konfigurácia Nextcloud zrušená", "Nextcloud gives you access to all your files wherever you are.": "Nextcloud vám umožní prístup ku všetkým súborom kdekoľvek ste.", - "Nextcloud volume size in GB": "Nextcloud veľkosť objemu v GB", + "Nextcloud volume size in GB": "Veľkosť dátového zväzku Nextcloud v GB", "Nextcloud with private PostgreSQL and Redis dependencies": "Nextcloud s súkromnými PostgreSQL a Redis závislé", "Nginx is an HTTP web server, reverse proxy, content cache, load balancer, TCP/UDP proxy server, and mail proxy server.": "Nginx je webový server HTTP, reverzný proxy, vyrovnávacia pamäť obsahu, načítací balander, proxy server TCP/UDP a proxy server pošty.", "Nginx webserver and reverse proxy with php support and a built-in Certbot (Let's Encrypt) client. It also contains fail2ban for intrusion prevention.": "Nginx webserver a reverzný proxy s php podporou a vstavaný Certbot (Let's Encrypt) klient. Obsahuje aj fail2ban na prevenciu vniknutia.", @@ -4776,7 +4776,7 @@ "PostgreSQL creates the database named in POSTGRES_DB on the first start. The installer default is postgresql.": "PostgreSQL vytvára databázu pomenovanú v POSTGRES_DB na prvom začiatku. Štandardný inštalátor je postgresql.", "PostgreSQL is an advanced, enterprise-class, and open-source relational database system. PostgreSQL supports both SQL (relational) and JSON (non-relational) querying.": "PostgreSQL je moderný, podnikový a open-source relačný databázový systém. PostgreSQL podporuje ako SQL (relatívne) tak JSON (nerelatívne) dotazovanie.", "PostgreSQL requires a password": "PostgreSQL requires heslo", - "PostgreSQL volume size in GB": "PostgreSQL veľkosť objemu v GB", + "PostgreSQL volume size in GB": "Veľkosť dátového zväzku PostgreSQL v GB", "Potential QEMU startup/assertion failures": "možné zlyhania štartu alebo assertion chyby QEMU", "Power state D3cold/D0 transitions may be inaccessible": "prechody napájacích stavov D3cold/D0 nemusia byť dostupné", "Powerful OCR powered by DeepSeek AI": "Výkonný OCR poháňaný DeepSeek AI", @@ -5631,7 +5631,7 @@ "Select archive": "Vyberte archív", "Select archive to restore": "Vyberte archív na obnovu", "Select at least one path to continue.": "Pre pokračovanie vyberte aspoň jednu cestu.", - "Select at least one stack container": "Vyberte aspoň jeden zásobníkový kontajner", + "Select at least one stack container": "Vyberte aspoň jeden kontajner aplikácie", "Select at least one suite application": "Vyberte aspoň jednu aplikáciu suite", "Select authentication mode:": "Vyberte spôsob prihlásenia:", "Select authentication type:": "Vyberte typ prihlásenia:", @@ -5832,16 +5832,16 @@ "Shared Group": "Zdieľaná skupina", "Shared directories": "Zdieľané priečinky", "Shared directory created:": "Vytvorený zdieľaný adresár:", - "Shared directory for consume and export": "Zdieľaný adresár pre spotrebu a export", - "Shared directory for copy/sync operations": "Zdieľaný adresár pre kopírovanie/sync operations", + "Shared directory for consume and export": "Zdieľaný adresár pre priečinky consume a export", + "Shared directory for copy/sync operations": "Zdieľaný adresár na kopírovanie a synchronizáciu", "Shared group: CONFIGURED": "Zdieľaná skupina: NASTAVENÁ", "Shared group: sharedfiles (GID:": "Zdieľaná skupina: sharedfiles (GID:", "Shared host content (not included in LXC backups):": "Zdieľaný obsah hostiteľa (nezahrnutý do záloh LXC):", "Shared host data is not reverted by the backup. Continue?": "Zdieľané dáta hostiteľa nie sú vrátené zálohou. Pokračovať?", "Shared host data is not reverted by the backups. Continue?": "Zdieľané dáta hostiteľa nie sú vrátené zálohami. Pokračovať?", "Shared host directories (not included in Proxmox backups)": "Zdieľané adresáre hostiteľov (nezahrnuté do záloh Proxmox)", - "Shared host directory": "Zdieľaný adresár hostiteľov", - "Shared host directory (not included in Proxmox backups)": "Zdieľaný adresár host (nie je súčasťou záloh Proxmox)", + "Shared host directory": "Zdieľaný adresár hostiteľa", + "Shared host directory (not included in Proxmox backups)": "Zdieľaný adresár hostiteľa (nie je v zálohách Proxmoxu)", "Shared host files are kept as they are; the backup does not restore their content.": "Zdieľané hostiteľské súbory sú uchovávané ako sú; záloha neobnovuje ich obsah.", "Shared host media directory": "Zdieľaný adresár médií pre hostiteľov", "Shared memory size for the GPU workload in MB": "Zdieľaná veľkosť pamäte pre pracovné zaťaženie GPU v MB", @@ -5913,7 +5913,7 @@ "Single target found — selected automatically:": "Našiel sa jeden target - vybraný automaticky:", "Size": "Veľkosť", "Size in GB of": "Veľkosť v GB pre", - "Size of each consume/export volume in GB": "Veľkosť každého spotrebovaného/vývozného objemu v GB", + "Size of each consume/export volume in GB": "Veľkosť každého dátového zväzku pre consume a export v GB", "Size of the /dev/shm shared memory in MB": "Veľkosť zdieľanej pamäte /dev/shm v MB", "Size of the Frigate temporary cache in MB": "Veľkosť Frigate dočasnej vyrovnávacej pamäte v MB", "Size:": "Veľkosť:", @@ -6111,7 +6111,7 @@ "Storage disk identifier:": "Identifikátor disku v úložisku:", "Storage for": "Úložisko pre", "Storage for Nextcloud files, configuration and data": "Úložisko pre Nextcloud súborov, konfigurácie a dát", - "Storage for Paperless data and documents": "Úložisko pre údaje a dokumenty bez dokumentov", + "Storage for Paperless data and documents": "Úložisko pre údaje a dokumenty aplikácie Paperless", "Storage for Tandoor files": "Úložisko pre súbory Tandoor", "Storage for persistent data": "Skladovanie trvalých údajov", "Storage for recipe images and files": "Skladovanie pre recepty a súbory", @@ -6288,7 +6288,7 @@ "The NVIDIA hook path does not belong to the installer": "Cesta k háku NVIDIA nepatrí inštalátorovi", "The NVIDIA installer needs at least": "NVIDIA inštalátor potrebuje aspoň", "The NVIDIA runtime is up to date; the container is not modified or started": "Runtime NVIDIA je aktuálny; kontajner nie je upravený ani spustený", - "The Nextcloud volume needs at least 8 GB": "Objem Nextcloud potrebuje najmenej 8 GB", + "The Nextcloud volume needs at least 8 GB": "Dátový zväzok Nextcloud musí mať najmenej 8 GB", "The OCI archive contains no SHA-256 blobs": "Archív OCI neobsahuje žiadne SHA-256 blobs", "The OCI archive does not contain exactly one manifest": "Archív OCI neobsahuje presne jeden manifest", "The OCI archive does not exist or is empty:": "Archív OCI neexistuje alebo je prázdny:", @@ -6299,9 +6299,9 @@ "The OCI image storage was not kept": "Uskladnenie obrazu OCI nebolo uchované", "The OCR language must use Tesseract codes, for example eng or eng+spa": "Jazyk OCR musí používať kódy Tesseract, napríklad eng alebo eng+spa", "The PATH of the new image is outside the reproducible profile": "PATH nového obrázku je mimo reprodukovateľného profilu", - "The Paperless persistent volumes need at least 8 GB": "Trvalé objemy bez dokumentov potrebujú najmenej 8 GB", + "The Paperless persistent volumes need at least 8 GB": "Trvalé dátové zväzky aplikácie Paperless musia mať najmenej 8 GB", "The PostgreSQL volume needs at least 4 GB": "Objem PostgreSQL potrebuje najmenej 4 GB", - "The PostgreSQL volume needs at least 8 GB": "Objem PostgreSQL potrebuje najmenej 8 GB", + "The PostgreSQL volume needs at least 8 GB": "Dátový zväzok PostgreSQL musí mať najmenej 8 GB", "The ProxMenux Monitor update checker is not available. Start or update ProxMenux Monitor and try again.": "Kontrola aktualizácií v ProxMenux Monitor nie je dostupná. Spustite alebo aktualizujte ProxMenux Monitor a skúste to znova.", "The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "Aktualizácia Proxmox VE 8 sa nedokončila. Prechod na Proxmox VE 9 sa nespustil.", "The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Chýba zväzok kľúčov archívu Proxmox;Inštalácia Ceph nemôže bezpečne pokračovať", @@ -6311,7 +6311,7 @@ "The Rclone configuration needs at least 1 GB": "Konfigurácia Rklonu potrebuje aspoň 1 GB", "The Rclone rootfs needs at least 2 GB": "Rclone rootfs potrebuje aspoň 2 GB", "The Selkies profile requires a verified LinuxServer image": "Profil Selkies requires a overený obraz LinuxServer", - "The Tandoor files volume needs at least 2 GB": "Objem súborov Tandoor potrebuje aspoň 2 GB", + "The Tandoor files volume needs at least 2 GB": "Dátový zväzok súborov Tandoor musí mať najmenej 2 GB", "The URL does not contain the required parameters (id, pack, edition).": "URL neobsahuje potrebné parametre (id, pack, edition).", "The USB number can change after reconnecting or rebooting. This profile does not remap it automatically or handle Coral USB re-enumeration. Do not share a dongle already used by another service.": "Číslo USB sa môže zmeniť po opätovnom pripojení alebo reštarte. Tento profil nie je automaticky premapovať alebo spracovať Coral USB re-enumeration. Nezdieľajte šišku, ktorú už používa iná služba.", "The Unifi-controller software is a powerful, enterprise wireless software engine ideal for high-density client deployments requiring low latency and high uptime performance.": "Softvér Unifi-controller je výkonný, podnikový bezdrôtový softvérový motor ideálny pre zavedenie klientov s vysokou hustotou requiring nízka latencia a vysoký výkon v čase.", @@ -6392,7 +6392,7 @@ "The configuration stopped because of an unexpected error": "Konfigurácia sa zastavila kvôli neočakávanej chybe", "The configured credential helper could not be used. Check its installation and credential store.": "Nakonfigurovaný pomocný program na správu prihlasovacích údajov sa nedá použiť. Skontrolujte jeho inštaláciu a úložisko prihlasovacích údajov.", "The console of the container could not be configured": "Konzolu kontajnera nebolo možné nakonfigurovať", - "The consume/export volumes need at least 1 GB": "Objem spotreby/vývozu potrebuje najmenej 1 GB", + "The consume/export volumes need at least 1 GB": "Dátové zväzky pre consume a export musia mať najmenej 1 GB", "The container could not be removed automatically:": "Nádoba sa nedala automaticky odstrániť:", "The container could not be started:": "Nádoba nemohla byť spustená:", "The container creation does not match the prepared instance": "Vytvorenie kontajnera nezodpovedá pripravenej inštancii", @@ -6607,7 +6607,7 @@ "The official startup cannot be reproduced": "Oficiálny štart nie je možné reprodukovať", "The official startup of the application is missing": "Chýba oficiálny začiatok aplikácie", "The operation already finished; it is not restored automatically": "Operácia už bola dokončená; jej predchádzajúci stav sa automaticky neobnoví", - "The operation could not be completed": "operation nebolo možné dokončiť", + "The operation could not be completed": "Operáciu sa nepodarilo dokončiť", "The operation stopped halfway. Choose \"Recover\" for this container in the OCI management menu to restore the previous installation.": "Operácia sa zastavila v polovici. Ak chcete obnoviť predchádzajúcu inštaláciu, v ponuke Správca OCI aplikácií pri tomto kontajneri vyberte \"Obnoviť\".", "The operation stopped halfway. Choose \"{choice}\" for this container in the OCI management menu.": "Operácia sa zastavila v polovici. V ponuke Správca OCI aplikácií pri tomto kontajneri vyberte \"{choice}\".", "The operation was stopped because a shared directory changed its identity:": "operation bol zastavený, pretože zdieľaný adresár zmenil svoju identitu:", @@ -6941,7 +6941,7 @@ "This tool is designed for systems with AMD GPUs.": "Tento nástroj je určený pre systémy s AMD GPU.", "This tool is designed for systems with Intel GPUs.": "Tento nástroj je určený pre systémy s Intel GPU.", "This translator only supports the Nextcloud stack": "Tento prekladateľ podporuje iba Nextcloud stack", - "This value is required.": "Táto hodnota je required.", + "This value is required.": "Táto hodnota je povinná.", "This variant requires the device": "Tento variant vyžaduje zariadenie", "This version does not build against the running kernel.": "Táto verzia nie je postavená proti bežiacemu jadru.", "This will RESET the ProxMenux Monitor login credentials on this host:": "Týmto sa vynulujú prihlasovacie údaje do ProxMenux Monitora na tomto serveri:", @@ -7689,7 +7689,7 @@ "Zotero is a free, easy-to-use tool to help you collect, organize, annotate, cite, and share research.": "Zotero je bezplatný, ľahko použiteľný nástroj, ktorý vám pomôže zhromažďovať, organizovať, anotovať, citovať a zdieľať výskum.", "a device it asks for cannot be translated:": "požadované zariadenie nemožno pripojiť do LXC:", "a different host monitor include already exists:": "už existuje iný include hostiteľského monitora:", - "a value is required": "hodnota je required", + "a value is required": "hodnota je povinná", "aMule WebUI (password only, no username)": "aMule WebUI (iba heslo, žiadne užívateľské meno)", "aMule is a multi-platform client for the ED2K file sharing network and based on the windows client eMule. aMule started in August 2003, as a fork of xMule, which is a fork of lMule.": "aMule je multiplatformový klient pre sieť zdieľania súborov ED2K a založený na eMule klienta okien. aMule začal v auguste 2003, ako fork xMule, čo je fork lMule.", "active VF(s)": "aktívne VF", @@ -8109,7 +8109,7 @@ "started successfully.": "úspešne spustený.", "started.": "spustený.", "startup/restart errors are likely.": "pravdepodobne narazí na chyby štartu/reštartu.", - "staticfiles volume size in GB": "veľkosť statických súborov v GB", + "staticfiles volume size in GB": "Veľkosť dátového zväzku staticfiles v GB", "stop source VM first": "najprv zastavte zdrojovú VM", "stopped": "zastavené", "storage yet.": "úložisko zatiaľ.", From 092a62e2dd164fc494fb85c5163e82b7c059e7f6 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 18:05:20 +0000 Subject: [PATCH 20/27] i18n: preserve consume directory name in OCI storage question --- .github/scripts/tests/test_oci_storage_sk.py | 1 + lang/sk.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/scripts/tests/test_oci_storage_sk.py b/.github/scripts/tests/test_oci_storage_sk.py index df1d83bc..ba88229c 100644 --- a/.github/scripts/tests/test_oci_storage_sk.py +++ b/.github/scripts/tests/test_oci_storage_sk.py @@ -79,6 +79,7 @@ class StorageWordingTests(StorageFixture): ui,plan=self.build('paperless-ngx',{'Documents volume size in GB':'30', 'Where to store the consume and export folders':'managed-volume','Size of each consume/export volume in GB':'3'}) for text in ['Úložisko pre údaje a dokumenty aplikácie Paperless', + 'Kam uložiť priečinky consume a export', 'Veľkosť dátového zväzku pre dokumenty v GB', 'Veľkosť každého dátového zväzku pre consume a export v GB']: self.assertIn(text,ui.asked) diff --git a/lang/sk.json b/lang/sk.json index 5e6520b8..e39cd5ab 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -7564,7 +7564,7 @@ "Where to store": "Kde uchovávať", "Where to store the Immich library": "Kde uložiť knižnicu Immich", "Where to store the Nextcloud files, configuration and data": "Kam uložiť súbory, konfiguráciu a dáta Nextcloud", - "Where to store the consume and export folders": "Kam uložiť priečinky spotreby a exportu", + "Where to store the consume and export folders": "Kam uložiť priečinky consume a export", "Where to store the recipe images and files": "Kam ukladať obrázky a súbory receptov", "Which guests should this job back up?": "Ktorých hostí by mala táto úloha zálohovať?", "While the server allows guest listing, no shares are actually accessible without authentication.": "Server síce dovolí hosťovi zobraziť zoznam, ale bez prihlásenia nie je dostupné žiadne zdieľanie.", From 37a6c02111cad196ef4448b0c710e0aaac1f21a9 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 18:48:01 +0000 Subject: [PATCH 21/27] i18n: complete OCI catalogue and wizard Slovak wording --- .../scripts/tests/test_oci_complete_ui_sk.py | 148 +++ lang/sk.json | 937 +++++++++--------- oci/src/proxmenux_oci/cli.py | 12 +- oci/src/proxmenux_oci/i18n.py | 18 + oci/src/proxmenux_oci/stack.py | 6 +- oci/tests/test_immich_acceleration.py | 11 +- 6 files changed, 655 insertions(+), 477 deletions(-) create mode 100644 .github/scripts/tests/test_oci_complete_ui_sk.py diff --git a/.github/scripts/tests/test_oci_complete_ui_sk.py b/.github/scripts/tests/test_oci_complete_ui_sk.py new file mode 100644 index 00000000..6d56db3b --- /dev/null +++ b/.github/scripts/tests/test_oci_complete_ui_sk.py @@ -0,0 +1,148 @@ +"""Real catalogue/wizard rendering, with host and installation effects forbidden.""" +import copy +from contextlib import ExitStack, redirect_stdout +import io +import json +import os +from pathlib import Path +import subprocess +import sys +import unittest +from unittest.mock import Mock, patch + +ROOT = Path(os.environ.get('OCI_I18N_TEST_ROOT', Path(__file__).resolve().parents[3])) +sys.path[:0] = [str(ROOT / 'oci/src'), str(ROOT / 'oci/remote'), str(ROOT / 'oci/tests')] +from proxmenux_oci import cli, i18n, installer, stack +from proxmenux_oci.catalog import Catalog +from proxmenux_oci.converter import ConversionError +from proxmenux_oci.ui import UserCancelled +from test_advanced_flow_order import RecordingUI, addresses, storages + + +class CompleteUI(unittest.TestCase): + def setUp(self): + self.scope = ExitStack() + self.addCleanup(self.scope.close) + self.sk = json.loads((ROOT / 'lang/sk.json').read_text()) + self.catalog = Catalog(ROOT / 'oci') + self.patch(i18n, '_language', 'sk') + self.patch(i18n, '_cache', self.sk) + self.patch(installer.host, 'storages', side_effect=storages) + self.patch(installer.host, 'bridges', return_value=[{'iface': 'vmbr0', 'cidr': '192.0.2.10/24'}]) + self.patch(installer.host, 'timezone', return_value='Europe/Bratislava') + self.patch(installer.host, 'gpus', return_value={'intel': [], 'amd': [], 'nvidia': False}) + self.patch(installer.access, 'ask_addresses', side_effect=addresses) + self.patch(installer.access, 'ask_ipv4', return_value=('dhcp', None)) + self.effects = [] + for module, name in [(cli, '_install'), (cli, '_rclone_mount'), (cli, 'run_remote_install'), + (installer, 'run_remote_install'), (cli.images, 'offer_removal'), + (subprocess, 'run'), (subprocess, 'Popen'), (subprocess, 'check_output'), + (Path, 'write_text'), (Path, 'write_bytes'), (Path, 'unlink')]: + self.effects.append(self.patch(module, name, side_effect=AssertionError('Forbidden effect: ' + name))) + self.addCleanup(lambda: [effect.assert_not_called() for effect in self.effects]) + + def patch(self, obj, name, *args, **kwargs): + return self.scope.enter_context(patch.object(obj, name, *args, **kwargs)) + + def test_search_no_match_uses_application_wording_and_never_installs(self): + ui = Mock() + ui.ask.return_value = 'fiction-no-such-app' + cli._search(self.catalog, ui, []) + self.assertEqual(ui.ask.call_args.args[0], 'Názov alebo časť popisu aplikácie') + self.assertIn('Žiadna zhoda aplikácií', ui.message.call_args.args[0]) + ui.choose.assert_not_called() + + def test_immich_detail_translates_service_roles_without_changing_template(self): + template = self.catalog.compose('immich') + before = copy.deepcopy(template) + text = cli._app_detail_text(self.catalog, self.catalog.find_item('immich'), template) + self.assertIn('Strojové učenie', text) + self.assertIn('Aplikácia', text) + self.assertIn('Oficiálny obraz', text) + self.assertNotIn('Machine learning', text) + self.assertEqual(template, before) + with patch.object(i18n, '_language', 'en'): + self.assertIn('Machine learning', cli._app_detail_text(self.catalog, self.catalog.find_item('immich'), template)) + + def test_missing_catalogue_entry_preserves_raw_error_and_localizes_cli(self): + for language, expected in [('sk', "Aplikácia 'fiction-missing' nie je v indexe"), + ('en', "The application 'fiction-missing' is not in the index")]: + with self.subTest(language=language), patch.object(i18n, '_language', language): + with self.assertRaisesRegex(ConversionError, "The application 'fiction-missing' is not in the index"): + self.catalog.find_item('fiction-missing') + output = io.StringIO() + with redirect_stdout(io.StringIO()), patch.object(sys, 'stderr', output), patch.object(cli, 'PROJECT_ROOT', ROOT / 'oci'): + self.assertEqual(cli.main(['show', 'fiction-missing']), 1) + self.assertEqual(output.getvalue().strip(), 'ERROR: ' + expected) + + def test_missing_template_uses_existing_translation_and_keeps_identifier(self): + self.patch(self.catalog, 'find_item', return_value={'id': 'fiction-missing'}) + with self.assertRaises(ConversionError) as caught: + self.catalog.compose('fiction-missing') + self.assertEqual(str(caught.exception), "No template is available for 'fiction-missing'") + self.assertEqual(i18n.diagnostic(caught.exception), "Pre aplikáciu 'fiction-missing' nie je dostupná šablóna") + + def test_detail_cancel_and_escape_do_not_enter_install_or_rclone(self): + for app in ('jellyfin', 'rclone', 'immich', 'suite-arr'): + for escape in (False, True): + with self.subTest(app=app, escape=escape): + ui = Mock() + ui.confirm.return_value = False + ui.detail_menu.return_value = None + if escape: + ui.detail_menu.side_effect = UserCancelled() + try: + cli._app_detail(self.catalog, ui, self.catalog.find_item(app)) + except UserCancelled: + self.assertTrue(escape) + + def test_special_wizard_plans_are_identical_in_english_and_slovak(self): + for app in ('immich', 'nextcloud-stack', 'paperless-ngx', 'tandoor'): + for mode in (installer.DEFAULT_MODE, installer.ADVANCED_MODE): + with self.subTest(app=app, mode=mode): + template = self.catalog.compose(app) + before = copy.deepcopy(template) + plan_sk = installer.build_deployment(template, RecordingUI(), mode) + with patch.object(i18n, '_language', 'en'): + plan_en = installer.build_deployment(template, RecordingUI(), mode) + self.assertEqual(plan_sk, plan_en) + self.assertEqual(template, before) + text = cli._deployment_summary_text(template, plan_sk) + self.assertNotIn('Consume/export:', text) + if app == 'paperless-ngx': + self.assertIn('Priečinky consume a export', text) + + def test_romm_optional_credentials_are_localized_but_variable_names_stay(self): + ui = RecordingUI() + ui.confirm = lambda text, default=False, **kwargs: (ui.asked.append(text) or text.startswith('Nastaviť IGDB')) + ui.password = lambda text, **kwargs: (ui.asked.append(text) or 'fiction-secret') + ui.ask = lambda text, default=None, **kwargs: (ui.asked.append(text) or (default if default is not None else 'fiction-value')) + plan = installer.build_deployment(self.catalog.compose('romm'), ui, installer.ADVANCED_MODE) + self.assertTrue(any('Tajný kľúč klienta IGDB' == text for text in ui.asked)) + self.assertFalse(any('IGDB Client Secret' == text for text in ui.asked)) + server = next(service for service in plan['services'] if service['name'] == 'romm') + self.assertTrue(any(row['name'] == 'IGDB_CLIENT_SECRET' for row in server['deployment']['environment'])) + + def test_tandoor_warning_keeps_negative_requirement_and_first_run_account(self): + template = self.catalog.compose('tandoor') + warning = i18n.translate(template['proxmox']['security_profile']['warning']) + self.assertIn('nevyžaduje privilegovaný', warning) + credentials = template['first_run']['credentials'] + self.assertTrue(any('Vygenerovaný účet správcu Tandoor' == i18n.translate(row['label']) for row in credentials)) + + def test_documented_commands_paths_and_literals_remain_copyable(self): + fragments = { + 'rsnapshot starts with the default configuration, which backs up /data into /.snapshots. Edit /config/rsnapshot.conf inside the container to set your own backup points, snapshot root and retention intervals.': ['/data', '/.snapshots', '/config/rsnapshot.conf'], + 'Each /request command needs a backend: add a [[backends]] block in the same file with the url and api_key of your Sonarr, Radarr or Seerr instance, then restart the container.': ['/request', '[[backends]]', 'url', 'api_key'], + 'Named accounts need private mode. Stop the container, set public: false in /config/config.js, start it again and create each user with: pct exec -- env THELOUNGE_HOME=/config s6-setuidgid abc thelounge add ': ['public: false', 'pct exec -- env THELOUNGE_HOME=/config s6-setuidgid abc thelounge add '], + 'Certificate provider: empty for Let\'s Encrypt, zerossl for ZeroSSL': ["Let's Encrypt", 'zerossl', 'ZeroSSL'], + 'Libraries: /data/media/movies, /data/media/series and /data/media/music. Select them in the media server.': ['/data/media/movies', '/data/media/series', '/data/media/music'], + } + for source, tokens in fragments.items(): + with self.subTest(source=source): + for token in tokens: + self.assertIn(token, i18n.translate(source)) + + +if __name__ == '__main__': + unittest.main() diff --git a/lang/sk.json b/lang/sk.json index e39cd5ab..edd0df4f 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -1,4 +1,7 @@ { + "Application data": "Údaje aplikácie", + "Consume/export": "Priečinky consume a export", + "Library": "Knižnica", "# Alternative if you prefer screen": "# Alternatíva, ak preferujete screen", "# Recommended: avoids disconnection during upgrade": "# Odporúčané: zabráni odpojeniu počas aktualizácie", "(Checked entries will be removed. Uncheck to keep in VM.)": "(Označené položky sa odstránia. Zrušte označenie, ak ich chcete ponechať vo VM.)", @@ -42,7 +45,7 @@ ">> Create a new backup job <<": ">> Vytvoriť novú úlohu zálohovania <<", ">> Save and return <<": ">> Uložiť a vrátiť <<", "A GTK Broadway web UI for libvirt and virt-manager.": "GTK Broadway web UI pre libvirt a virt-manager.", - "A Personal Relationship Management tool to help you document your social life.": "Osobný vzťah Nástroj riadenia, ktorý vám pomôže zdokumentovať váš spoločenský život.", + "A Personal Relationship Management tool to help you document your social life.": "Nástroj na správu osobných vzťahov a zaznamenávanie spoločenského života.", "A VirtIO ISO already exists. Do you want to overwrite it?": "VirtIO ISO už existuje. Chcete ho prepísať?", "A ZFS pool with this name already exists.": "ZFS pool s týmto názvom už existuje.", "A ZFS pool with this name already exists:": "ZFS pool s týmto názvom už existuje:", @@ -56,10 +59,10 @@ "A coordinated operation is pending. The whole previous stack will be recovered, not only the selected member. If the operation already finished, the cleanup of its markers is completed.": "Koordinovaná operation čaká. Celý predchádzajúci stack bude obnovený, nielen vybraný člen. Ak už operation skončil, vyčistenie jeho markerov je ukončené.", "A different host monitor include already exists; it is not overwritten:": "Iný hostiteľský monitor už existuje; nie je prepísaný:", "A fancy monitoring tool": "Nádherný monitorovací nástroj", - "A free and open-source cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with schemata.": "zadarmo a open-source cross-platform dokument-orientovaný databázový program. Utajené ako NoSQL databázový program, MongoDB používa JSON-like dokumenty so schémou.", - "A free reverse proxy for tunneling services (not self-hosted).": "Voľný spätný proxy pre tunelovanie služby (nie je self-hosted).", - "A free, self-hostable news aggregator…": "Slobodný agregácia správ...", - "A full-featured, open-source AI chat interface": "Full-featured, open-source AI chat rozhranie", + "A free and open-source cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with schemata.": "MongoDB je bezplatná multiplatformová dokumentová databáza s otvoreným zdrojovým kódom. Patrí medzi databázy NoSQL a používa dokumenty podobné JSON so schémami.", + "A free reverse proxy for tunneling services (not self-hosted).": "Bezplatný reverzný proxy na tunelovanie služieb (neprevádzkuje sa na vlastnom serveri).", + "A free, self-hostable news aggregator…": "Bezplatný agregátor správ na vlastnom serveri…", + "A full-featured, open-source AI chat interface": "Plnohodnotné chatové rozhranie pre AI s otvoreným zdrojovým kódom", "A gasket DKMS registration is still present:": "Stále existuje registrácia gasket DKMS:", "A host bind mount cannot be included in vzdump": "Pripojenie hostiteľa nemôže byť súčasťou vzdumpu", "A host firewall rule is only valid for a host-monitor profile": "Pravidlo firewallu hostiteľa je platné iba pre profil monitorovania hostiteľa", @@ -94,7 +97,7 @@ "A pending stack assembly already exists; it is not overwritten": "Nevyriešená zostava stohov už existuje; nie je prepísaná", "A previous NVIDIA refresh is pending review": "Predchádzajúce obnovovanie NVIDIA čaká na preskúmanie", "A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "Pre nasledujúce GPU NVIDIA bola zistená predchádzajúca konfigurácia priechodu VFIO:", - "A privacy-first, open-source platform for knowledge management and collaboration.": "Platforma pre riadenie znalostí a spoluprácu na prvom mieste ochrany súkromia s otvoreným zdrojom.", + "A privacy-first, open-source platform for knowledge management and collaboration.": "Platforma na správu znalostí a spoluprácu s otvoreným zdrojovým kódom a dôrazom na súkromie.", "A reboot is recommended before the GPU is guaranteed to stay on the native driver.": "Pred zaručením, že GPU zostane na natívnom ovládači, sa odporúča reštartovať.", "A reboot is required after installation to load the new kernel modules.": "Po inštalácii je potrebný reštart, aby sa načítali nové kernel moduly.", "A reboot is required for VFIO binding to take effect. Do you want to restart now?": "Na uplatnenie VFIO naviazania je potrebný reštart. Chcete reštartovať teraz?", @@ -106,13 +109,13 @@ "A rootfs adaptation is stored in persistent storage": "Adaptácia korienkov sa skladuje v trvalom sklade", "A running container is restarted to apply the changes; a stopped one is left stopped. The data of the application, its database and the configuration of the other containers are not changed. A container volume removed here is deleted with the data in it.": "Spustený kontajner sa reštartuje, aby sa zmeny použili; zastavený zostane zastavený. Údaje aplikácie sa zachovajú. Databáza ani ostatné kontajnery sa nemenia. Odstránením trvalého úložiska kontajnera sa vymažú aj údaje, ktoré obsahuje.", "A scheduled run cannot answer:": "Plánované spustenie nemôže odpovedať:", - "A self-hosted Bitwarden server": "Self-hosted Bitwarden server", - "A self-hosted, goal-free habit tracking tool.": "Sebestačný, bezcieľový nástroj na sledovanie zvyku.", - "A self-improving AI agent with memory, skills, messaging, and a web dashboard.": "Self-zlepšenie AI agenta s pamäťou, zručnosti, správy, a webová palubná doska.", + "A self-hosted Bitwarden server": "Server Bitwarden na vlastnom serveri", + "A self-hosted, goal-free habit tracking tool.": "Nástroj na sledovanie návykov bez stanovovania cieľov, prevádzkovaný na vlastnom serveri.", + "A self-improving AI agent with memory, skills, messaging, and a web dashboard.": "Zdokonaľujúci sa agent AI s pamäťou, zručnosťami, komunikáciou a webovým panelom.", "A server reboot is recommended for all changes to take full effect.": "Odporúča sa reštart servera, aby sa všetky zmeny naplno prejavili.", "A shared directory was replaced during the installation": "Počas inštalácie bol vymenený zdieľaný adresár", "A shared source does not match its recorded identity": "Spoločný zdroj nezodpovedá jeho zaznamenanej identite", - "A simple, open-source file sharing host.": "Jednoduchý, open-source súbor zdieľanie hostiteľa.", + "A simple, open-source file sharing host.": "Jednoduchá služba na zdieľanie súborov s otvoreným zdrojovým kódom.", "A simple, private file server.": "Jednoduchý súkromný súborový server.", "A single matching image platform cannot be resolved": "Jedinú zodpovedajúcu obrazovú platformu nie je možné vyriešiť", "A single-platform OCI archive is required": "Jednoplatformový OCI archív je required", @@ -120,17 +123,17 @@ "A stack member has a different identity": "Člen stohu má inú identitu", "A system reboot is recommended to ensure all changes take effect.": "Odporúča sa reštart systému, aby sa všetky zmeny bezpečne prejavili.", "A third party companion app available to Plex server owners to allow their users to request, review and discover content.": "Aplikácia tretej strany pre majiteľov serverov Plex, ktorá umožňuje ich používateľom požadovať, skúmať a objavovať obsah.", - "A third-party client for self-hosted server and self-hosted server, remote access management interface, remote access to installed applications.": "Klient tretej strany pre self-hosted server a self-hosted server, vzdialený prístup k nainštalovaným aplikáciám.", + "A third-party client for self-hosted server and self-hosted server, remote access management interface, remote access to installed applications.": "Klient tretej strany pre vlastný server s rozhraním na správu vzdialeného prístupu a prístup k nainštalovaným aplikáciám.", "A tmpfs mount is not part of the journal; recovery blocked": "A tmpfs mount nie je súčasťou časopisu; zotavenie blokované", "A tmpfs mount overlaps another mount": "Pripojenie tmpfs sa prekrýva s inou montážou", "A tunneling daemon by Cloudflare that safely exposes your web servers into the internet.": "Tunelový démon Cloudflare, ktorý bezpečne vystavuje vaše webové servery na internet.", "A versatile file conversion tool that supports multiple formats.": "Univerzálny nástroj na konverziu súborov, ktorý podporuje viacero formátov.", "A web GUI client of Project V which supports VMess, VLESS, SS, SSR, Trojan, Tuic and Juicity protocols": "Webový GUI klient projektu V, ktorý podporuje protokoly VMess, VLESS, SS, SSR, Trojan, Tuic a Juicity", "A web app to listen Youtube audio source.": "Webová aplikácia na počúvanie zdroja zvuku Youtube.", - "A web app to manage your Two-Factor Authentication (2FA) accounts and generate their security codes": "Webová aplikácia na správu vašich dvoch autentifikácií (2FA) a generovanie ich bezpečnostných kódov", - "A web frontend for the motion daemon.": "Web frontend pre filmový démon.", + "A web app to manage your Two-Factor Authentication (2FA) accounts and generate their security codes": "Webová aplikácia na správu účtov dvojfaktorového overovania (2FA) a generovanie bezpečnostných kódov", + "A web frontend for the motion daemon.": "Webové rozhranie pre službu motion.", "A web-based file sharing and management protocol": "Webový protokol zdieľania a správy súborov", - "A well-designed cross-platform ChatGPT UI.": "Dobre navrhnutý cross-platform ChatGPT UI.", + "A well-designed cross-platform ChatGPT UI.": "Prehľadné multiplatformové rozhranie pre ChatGPT.", "ACL Status:": "Stav ACL:", "ACL permissions applied for local access for user:": "ACL oprávnenia boli nastavené pre lokálny prístup používateľa:", "ADVANCED SETTINGS COMPLETE": "POKROČILÉ NASTAVENIA SÚ HOTOVÉ", @@ -180,19 +183,19 @@ "Absolute path to a file or directory you want backed up:": "Absolútna cesta k súboru alebo priečinku, ktorý chcete zálohovať:", "Acceleration": "Zrýchlenie", "Acceleration configuration cancelled": "Konfigurácia zrýchlenia zrušená", - "Acceleration for CodeProject.AI": "Zrýchlenie pre CodeProject. AI", + "Acceleration for CodeProject.AI": "Zrýchlenie pre CodeProject.AI", "Acceleration for Faster Whisper": "Zrýchlenie pre Faster Whisper", "Acceleration for Immich smart recognition": "Zrýchlenie pre Immich inteligentné uznanie", "Acceleration for Ollama": "Zrýchlenie pre Ollama", "Acceleration for Piper": "Zrýchlenie pre Piper", "Acceleration for llama.cpp": "Zrýchlenie pre llama.cpp", "Accept routes from other nodes?": "Prijímať trasy z iných uzlov?", - "Accept this host monitoring profile?": "Prijmite tento monitorovací profil hostiteľa?", + "Accept this host monitoring profile?": "Prijať tento profil monitorovania hostiteľa?", "Access Scope:": "Rozsah prístupu:", "Access bridge": "Prístupový mostík", "Access bridge for Immich": "Prístupový mostík pre Immich", "Access bridge for Nextcloud": "Prístupový mostík pre Nextcloud", - "Access bridge for Paperless": "Prístupový mostík pre bezpapierové", + "Access bridge for Paperless": "Prístupový most pre Paperless", "Access bridge for Tandoor": "Prístupový mostík pre Tandoor", "Access could not be granted completely; the application may be unable to write to:": "Prístup nebolo možné úplne udeliť; aplikácia možno nebude môcť zapisovať do:", "Access could not be granted; the application cannot write to:": "Prístup nebolo možné udeliť; aplikácia nemôže písať na:", @@ -212,7 +215,7 @@ "Active Connections": "Aktívne pripojenia", "Active exports:": "Aktívne exporty:", "Active session:": "Aktívna relácia:", - "Actual device path on the host": "Skutočná dráha zariadenia na hostiteľovi", + "Actual device path on the host": "Skutočná cesta k zariadeniu na hostiteľovi", "Adaptation file too large": "Adaptačný súbor príliš veľký", "Adaptation file with unexpected permissions or owner": "Adaptačný súbor s neočakávanými oprávneniami alebo majiteľom", "Adblock & DNS": "Adblock a DNS", @@ -281,13 +284,13 @@ "Adding existing users to sharedfiles group...": "Pridávam existujúcich používateľov do skupiny sharedfiles...", "Adding iSCSI storage to Proxmox...": "Pridávam iSCSI úložisko do Proxmoxu...", "Adding new share to smb.conf...": "Pridávam nové zdieľanie do smb.conf...", - "Adding peers later means raising PEERS in /etc/pve/lxc/.conf and restarting the container. The existing peer keys are kept.": "Pridanie peerov neskôr znamená zvýšenie PEERS v /etc/pve/lxc/.conf a obnovenie kontajnera. Existujúce kľúče peer sú zachované.", + "Adding peers later means raising PEERS in /etc/pve/lxc/.conf and restarting the container. The existing peer keys are kept.": "Ďalších účastníkov pridáte zvýšením hodnoty PEERS v /etc/pve/lxc/.conf a reštartom kontajnera. Existujúce kľúče účastníkov zostanú zachované.", "Adding the mount points...": "Pridanie bodov pripojenia...", "Adding this NVMe as a PCIe device (via 'Add Controller or NVMe PCIe to VM') gives better performance.": "Pridanie tohto NVMe ako PCIe zariadenia (cez 'Pridať radič alebo NVMe PCIe do VM') dáva lepší výkon.", "Adding to /etc/fstab for permanent mounting...": "Pridávam do /etc/fstab pre trvalé pripojenie...", "Additional URL advertised by Plex (optional)": "Ďalšie URL inzerované Plex (voliteľné)", "Additional audio function(s) to be added": "Pridajú sa ďalšie audio funkcie", - "Additional media/GPU GIDs, comma-separated": "Dodatočné médiá/GPU GID, čiarovo oddelené", + "Additional media/GPU GIDs, comma-separated": "Ďalšie GID pre médiá a GPU oddelené čiarkami", "Additional paths for": "Ďalšie cesty pre", "Address BookStack will be opened at (for example http://192.168.1.50)": "Adresa, na ktorej bude BookStack dostupný (napríklad http://192.168.1.50)", "Address Healthchecks will be opened at (for example http://192.168.1.50:8000)": "Adresa, na ktorej bude Healthchecks dostupný (napríklad http://192.168.1.50:8000)", @@ -304,7 +307,7 @@ "Address used to reach wallabag (http://IP or https://wallabag.example.com)": "Adresa použitá na dosiahnutie wallabag (http://IP alebo https://wallabag.example.com)", "Addresses to update: ipv4, ipv6 or both (uses an external service)": "Adresy na aktualizáciu: ipv4, ipv6 alebo oboje (používa externú službu)", "Adguardhome Sync web interface": "Webové rozhranie Adguardhome Sync", - "Adguardhome-sync is a tool to synchronize AdGuardHome config to replica instances.": "Adguardhome-sync je nástroj na synchronizáciu adGuardHome config na opakovanie inštancií.", + "Adguardhome-sync is a tool to synchronize AdGuardHome config to replica instances.": "Adguardhome-sync synchronizuje nastavenia AdGuard Home do ďalších inštancií.", "Adjust network/CIDR to your environment.": "Upravte sieť/CIDR podľa svojho prostredia.", "Adjust options if needed (vers=4,hard,timeo,...).": "Podľa potreby upravte možnosti (vers=4,hard,timeo,...).", "Adjusting systemd-journald limits to match Log2RAM size...": "Upravujem limity systemd-journald podľa veľkosti Log2RAM...", @@ -333,7 +336,7 @@ "After the reboot, you will only be able to access the Proxmox host via:": "Po reštarte sa k Proxmox hostovi dostanete iba cez:", "After this LXC → VM switch, reboot the host so the new binding state is applied cleanly.": "Po tomto prepnutí LXC → VM reštartujte host, aby sa nové naviazanie použilo čisto.", "Airsonic Advanced web interface": "Webové rozhranie Airsonic Advanced", - "Airsonic-advanced is a free, web-based media streamer, providing ubiquitious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.": "Airsonic-pokročilé je zadarmo, web-založené mediálne streamer, poskytuje ubiquitious prístup k vašej hudbe. Pomocou neho zdieľať hudbu s priateľmi, alebo počúvať svoju vlastnú hudbu v práci. Môžete prúdiť do viacerých hráčov súčasne, napríklad do jedného hráča v kuchyni a ďalšie vo vašej obývacej izbe.", + "Airsonic-advanced is a free, web-based media streamer, providing ubiquitious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.": "Airsonic-advanced je bezplatný webový server na streamovanie hudby. Umožňuje zdieľať hudbu s priateľmi alebo počúvať vlastnú zbierku aj v práci. Podporuje súčasné prehrávanie na viacerých zariadeniach, napríklad v kuchyni a obývačke.", "Aliases added to .bashrc": "Aliasy boli pridané do .bashrc", "Alist Sync web interface": "Alist Sync webové rozhranie", "All": "Všetky", @@ -368,7 +371,7 @@ "Allocate CPU Cores": "Prideliť CPU jadrá", "Allocate RAM in MiB": "Prideliť RAM v MiB", "Allow TCP port {port} from {subnet} through the host firewall? Existing firewall rules are not changed.": "Povoliť cez firewall hostiteľa TCP port {port} zo siete {subnet}? Existujúce pravidlá firewallu sa nemenia.", - "Allowed hosts (comma separated; * allows access through the assigned IP)": "Povolené hostiteľov (koma oddelené; * umožňuje prístup cez pridelený IP)", + "Allowed hosts (comma separated; * allows access through the assigned IP)": "Povolené názvy hostiteľov oddelené čiarkami (* povolí prístup cez pridelenú IP adresu)", "Allowed source subnet:": "Povolená zdrojová podsieť:", "Allowed web port:": "Povolený webový port:", "Already Mounted": "Už je pripojené", @@ -378,24 +381,24 @@ "Also comment any remaining 'bookworm' entries in *.list if present.": "Zakomentujte aj všetky zostávajúce položky 'bookworm' v *.list, ak existujú.", "Also install the VirtIO network driver during setup to enable network access.": "Počas inštalácie nainštalujte aj sieťový ovládač VirtIO, aby fungoval prístup k sieti.", "Although VFIO can bind to this device, full passthrough to a VM is": "Aj keď sa VFIO vie na toto zariadenie naviazať, úplný passthrough do VM je", - "Altus is an Electron-based WhatsApp client with themes and multiple account support.": "Altus je Electron-based WhatsApp klient s témami a viacerými podporou účtu.", + "Altus is an Electron-based WhatsApp client with themes and multiple account support.": "Altus je klient WhatsApp založený na Electrone s podporou tém a viacerých účtov.", "Ambiguous mount points in the container": "Ambiciózne body upevnenia v kontajneri", "Ambiguous or invalid environment variable": "Ambiciózna alebo neplatná premenná prostredia", "Amount of RAM in MiB (default: 4096)": "Množstvo RAM v MiB (predvolené: 4096)", "An AI model used to generate images conditioned on text descriptions.": "Model AI používaný na generovanie obrázkov podmienených textovými popismi.", "An AMD dedicated GPU has been detected without FLR support": "Bola zistená dedikovaná AMD GPU bez podpory FLR", "An AMD integrated GPU (APU) has been detected": "Bola zistená integrovaná AMD GPU (APU)", - "An Alist storage synchronization tool based on the Web interface.": "Nástroj na synchronizáciu pamäte Alist založený na webovom rozhraní.", - "An Industrial-Level Controllable and Efficient Zero-Shot Text-To-Speech System": "Priemyselná úroveň ovládateľná a efektívna zero-Shot Text-To-Speech System", + "An Alist storage synchronization tool based on the Web interface.": "Nástroj na synchronizáciu úložísk Alist cez webové rozhranie.", + "An Industrial-Level Controllable and Efficient Zero-Shot Text-To-Speech System": "Efektívny a ovládateľný systém prevodu textu na reč priemyselnej úrovne bez predchádzajúceho trénovania na cieľovom hlase", "An Intel dedicated GPU has been detected without FLR support": "Bola zistená dedikovaná Intel GPU bez podpory FLR", - "An accelerated video generation framework that speeds up end-to-end diffusion while preserving video quality": "Zrýchlený rámec tvorby videa, ktorý urýchľuje šírenie do konca a zároveň zachováva kvalitu videa", + "An accelerated video generation framework that speeds up end-to-end diffusion while preserving video quality": "Rámec na zrýchlené generovanie videa, ktorý urýchľuje celý difúzny proces pri zachovaní kvality videa", "An application changed since the update check. Check for updates again.": "Od poslednej kontroly sa aplikácia zmenila. Znova vyhľadajte aktualizácie.", "An executable required by the adapter is missing in the new image": "V novom obrázku chýba spustiteľný requi.", "An fstab entry already exists for:": "Záznam vo fstab už existuje pre:", "An image probe container was started externally": "Externe bola spustená obrazová sonda", "An include is not part of the journal; recovery blocked": "Zahrnutie nie je súčasťou časopisu; obnova zablokovaná", "An include was modified outside the journal; recovery blocked": "Zahrnutie bolo upravené mimo časopisu; regenerácia zablokovaná", - "An open source generative AI development platform for building AI Agents and LLM workflows": "Open source roding AI developing platform for building AI Agents and LLM workflows", + "An open source generative AI development platform for building AI Agents and LLM workflows": "Vývojová platforma generatívnej AI s otvoreným zdrojovým kódom na tvorbu agentov AI a pracovných postupov s LLM", "An operation of this installation has not finished; recover it from the management menu before removing it": "Operácia tejto inštalácie sa ešte nedokončila; pred odstránením ju obnovte v ponuke správy OCI", "An update does not accept configuration changes": "Aktualizácia neprijíma zmeny konfigurácie", "Analysis Tools": "Analytické nástroje", @@ -420,11 +423,11 @@ "Another node, typed by hand": "Ďalší uzol, zadaný ručne", "Another registry account operation is running. Try again when it finishes.": "Prebieha iná operácia registrového účtu. Skúste to znova, keď to skončí.", "Another stack operation is pending": "Iná operácia celej aplikácie ešte nie je dokončená", - "Application": "Uplatňovanie", + "Application": "Aplikácia", "Application registered on this host:": "Aplikácia registrovaná na tomto hostiteľovi:", "Application responding:": "Aplikácia odpovedá:", "Application responding; checking its stability...": "Aplikácia reaguje; kontrola jej stability...", - "Application suite: one independent LXC per selected application": "Application suite: jeden nezávislý LXC na vybranú aplikáciu", + "Application suite: one independent LXC per selected application": "Zostava aplikácií: samostatný kontajner LXC pre každú vybranú aplikáciu", "Application:": "Použitie:", "Applications you can choose:": "Aplikácie si môžete vybrať:", "Apply": "Použiť", @@ -438,9 +441,9 @@ "Apply fix now?": "Použiť opravu teraz?", "Apply fix now? (The share will be briefly remounted)": "Použiť opravu teraz? (zdieľanie sa na chvíľu znovu pripojí)", "Apply network optimizations": "Použiť sieťové optimalizácie", - "Apply optional security relaxation apparmor:rootlesskit": "Aplikovať voliteľný bezpečnostný relaxačný apartor:rootlesskit", - "Apply optional security relaxation apparmor:unconfined": "Aplikovať voliteľný bezpečnostný relaxačný apparmor:nekonfigurované", - "Apply optional security relaxation seccomp:unconfined": "Aplikovať voliteľné bezpečnostné relaxačné seccomp:unconfined", + "Apply optional security relaxation apparmor:rootlesskit": "Použiť voliteľné uvoľnenie bezpečnostných obmedzení apparmor:rootlesskit", + "Apply optional security relaxation apparmor:unconfined": "Použiť voliteľné uvoľnenie bezpečnostných obmedzení apparmor:unconfined", + "Apply optional security relaxation seccomp:unconfined": "Použiť voliteľné uvoľnenie bezpečnostných obmedzení seccomp:unconfined", "Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Použiť možnosti z aktuálnej šablóny katalógu? Môžu sa vyžadovať nové požadované cesty a nastavenia. Pred opätovným vytvorením CT skontrolujte výslednú konfiguráciu.", "Apply read+write access for 'others' on the host directory?": "Nastaviť pre 'others' čítanie aj zápis na priečinku hosta?", "Apply the options from the current catalog template? Your data and configuration are kept.": "Aplikovať možnosti z aktuálnej katalógovej šablóny? Vaše dáta a konfigurácia sú uchovávané.", @@ -464,7 +467,7 @@ "Applying the LAN address to the application URLs...": "Aplikovať adresu LAN na URL aplikácie...", "Applying the initial Nextcloud settings...": "Aplikácia prvých nastavení Nextcloud...", "Applying the mount mode...": "Aplikácia režimu pripojenia...", - "Apprise-api Takes advantage of Apprise through your network with a user-friendly API.": "Apprese-api Využíva aplikáciu Apprise prostredníctvom vašej siete s užívateľsky prívetivým API.", + "Apprise-api Takes advantage of Apprise through your network with a user-friendly API.": "Apprise-api sprístupňuje funkcie Apprise cez sieť pomocou jednoduchého API.", "Architecture": "Architektúra", "Architecture:": "Architektúra:", "Architectures": "Architektúry", @@ -474,7 +477,7 @@ "Archive ready": "Archív pripravený", "Archive size:": "Veľkosť archívu:", "Archive:": "Archív:", - "Ardour is an open source, collaborative effort of a worldwide team including musicians, programmers, and professional recording engineers.": "Ardour je otvorený zdroj spolupráce celosvetového tímu vrátane hudobníkov, programátorov a profesionálnych nahrávacích inžinierov.", + "Ardour is an open source, collaborative effort of a worldwide team including musicians, programmers, and professional recording engineers.": "Ardour je projekt s otvoreným zdrojovým kódom, na ktorom spolupracuje celosvetový tím hudobníkov, programátorov a profesionálnych zvukárov.", "Are you absolutely sure?": "Ste si úplne istý?", "Are you sure you want to continue?": "Naozaj chcete pokračovať?", "Are you sure you want to delete this export?": "Naozaj chcete vymazať tento export?", @@ -486,7 +489,7 @@ "Are you sure you want to unmount this NFS share?": "Naozaj chcete odpojiť toto NFS zdieľanie?", "Are you sure you want to unmount this Samba share?": "Naozaj chcete odpojiť toto Samba zdieľanie?", "Are you sure?": "Ste si istý?", - "Arr suite: applications to install": "Arr suite: aplikácie na inštaláciu", + "Arr suite: applications to install": "Zostava Arr: aplikácie na inštaláciu", "As Proxmox storage": "Ako úložisko Proxmoxu", "As host fstab mount only": "Iba ako mount v host fstab", "Assign GPU PCI function to VM": "Priradiť PCI funkciu GPU k VM", @@ -505,9 +508,9 @@ "Attempting automatic repair...": "Skúšam automatickú opravu...", "Attempting passthrough with this GPU typically results in": "Pokus o passthrough s touto GPU zvyčajne vedie k", "Attention: Removing the subscription banner may cause issues in the web interface after a future update.": "Upozornenie: odstránenie hlásenia o predplatnom môže po budúcej aktualizácii spôsobiť problém vo webovom rozhraní.", - "Audacity is an easy-to-use, multi-track audio editor and recorder. Developed by a group of volunteers as open source.": "Audacity je ľahko použiteľný, multi-track audio editor a rekordér. Vyvinutá skupinou dobrovoľníkov ako open source.", - "Audio device directory": "Adresár audio zariadenia", - "Audiobookshelf is a self-hosted audiobook and podcast server.": "Audiobookshelf je samoobsluha audioknihy a podcast server.", + "Audacity is an easy-to-use, multi-track audio editor and recorder. Developed by a group of volunteers as open source.": "Audacity je jednoduchý viacstopový zvukový editor a rekordér, ktorý dobrovoľníci vyvíjajú ako softvér s otvoreným zdrojovým kódom.", + "Audio device directory": "Adresár zvukových zariadení", + "Audiobookshelf is a self-hosted audiobook and podcast server.": "Audiobookshelf je server na audioknihy a podcasty, ktorý prevádzkujete sami.", "Audit completed. Press Enter to continue...": "Audit dokončený. Pokračujte stlačením Enter...", "Audit socket disabled or not required": "Zásuvka auditu je zakázaná alebo sa nevyžaduje", "Auth key is required.": "Auth key je povinný.", @@ -533,10 +536,10 @@ "Auto-sync was not enabled": "Automatická synchronizácia nebola zapnutá", "Automated Post-Install Script": "Automatický skript po inštalácii", "Automated post-installation script": "Automatický skript po inštalácii", - "Automatic Video Library Manager for TV Shows. It watches for new episodes of your favorite shows, and when they are posted it does its magic.": "Automatický správca videoknižnice pre televízne programy. Sleduje nové epizódy vašich obľúbených šou, a keď sú zverejnené to robí jeho mágiu.", + "Automatic Video Library Manager for TV Shows. It watches for new episodes of your favorite shows, and when they are posted it does its magic.": "Automatický správca knižnice televíznych seriálov. Sleduje nové epizódy obľúbených seriálov a spracuje ich, keď sú dostupné.", "Automatic detection": "Automatická detekcia", "Automatic private network allocation requires a /24 subnet": "Automatické pridelenie súkromných sietí requires a /24 subnet", - "Automatic video library manager for TV Shows": "Automatický správca video knižnice pre TV Shows", + "Automatic video library manager for TV Shows": "Automatický správca knižnice televíznych seriálov", "Automatic/Unattended": "Automaticky/bez zásahu", "Automation & Scheduling": "Automatizácia a plánovanie", "Available": "Dostupné", @@ -569,14 +572,14 @@ "BRIDGE CONFIGURATION ANALYSIS": "ANALÝZA NASTAVENIA BRIDGE", "BTRFS:": "BTRFS:", "Baby Buddy web interface": "Webové rozhranie Baby Buddy", - "Babybuddy is a buddy for babies! Helps caregivers track sleep, feedings, diaper changes, tummy time and more to learn about and predict baby's needs without (as much) guess work.": "Babybuddy je kamarát pre deti! Pomáha opatrovateľom sledovať spánok, kŕmenie, zmeny plienky, čas bruška a ďalšie učiť sa a predvídať potreby dieťaťa bez (tak veľa) hádať prácu.", + "Babybuddy is a buddy for babies! Helps caregivers track sleep, feedings, diaper changes, tummy time and more to learn about and predict baby's needs without (as much) guess work.": "Baby Buddy pomáha pri starostlivosti o dieťa sledovaním spánku, kŕmenia, výmen plienok, času na brušku a ďalších činností. Uľahčuje porozumenie potrebám dieťaťa a ich predvídanie.", "Back": "Späť", "Back to job list": "Späť na zoznam úloh", "Back to previous menu or Esc + Enter": "Späť do predchádzajúceho menu alebo Esc + Enter", "Backed up and cleared": "Zálohovaná a vyčistená", "Backend": "Backend", "Backend:": "Backend:", - "Background archive extraction for Arr download queues. No web interface.": "Extrakcia archívu pozadia pre Arr download fronty. Žiadne webové rozhranie.", + "Background archive extraction for Arr download queues. No web interface.": "Rozbaľovanie archívov na pozadí pre fronty sťahovania aplikácií Arr. Bez webového rozhrania.", "Backup — VM and CT backups": "Backup — zálohy VM a CT", "Backup & Recovery": "Zálohovanie a obnova", "Backup Created": "Záloha bola vytvorená", @@ -624,7 +627,7 @@ "Backups already on PBS were encrypted with the current key — downloading them will fail unless you first Download the current keyfile to keep a copy.": "Zálohy, ktoré už sú v PBS, boli zašifrované aktuálnym kľúčom - ich stiahnutie zlyhá, ak si najprv nestiahnete a neodložíte aktuálny keyfile.", "Backups already stored on PBS were encrypted with the current keyfile. After this action:": "Zálohy, ktoré sú už uložené v PBS, boli zašifrované aktuálnym keyfile. Po tejto akcii:", "Backups verified": "Potvrdené zálohy", - "Bambu Studio is an open-source, cutting-edge, feature-rich slicing software. It contains project-based workflows, systematically optimized slicing algorithms, and an easy-to-use graphical interface, bringing users an incredibly smooth printing experience.": "Bambu Studio je open-source, špičkový, funkcie-bohaté krájanie softvér. Obsahuje projektové pracovné postupy, systematicky optimalizované algoritmy krájania a ľahko použiteľné grafické rozhranie, ktoré používateľom prináša neuveriteľne hladký zážitok z tlače.", + "Bambu Studio is an open-source, cutting-edge, feature-rich slicing software. It contains project-based workflows, systematically optimized slicing algorithms, and an easy-to-use graphical interface, bringing users an incredibly smooth printing experience.": "Bambu Studio je moderný a funkčne bohatý nástroj s otvoreným zdrojovým kódom na prípravu vrstiev pre 3D tlač. Ponúka projektové pracovné postupy, optimalizované algoritmy a jednoduché grafické rozhranie na plynulú prípravu tlače.", "Bandwidth limit configured": "Limit priepustnosti je nastavený", "Bandwidth test (iperf3)": "Test priepustnosti (iperf3)", "Bandwidth test completed successfully": "Test priepustnosti bol úspešne dokončený", @@ -632,27 +635,27 @@ "Base VMID": "Základné VMID", "Base VMID (empty = next free block)": "Základné VMID (prázdne = ďalší voľný blok)", "Base VMID of Nextcloud (empty = next free block)": "Základné VMID Nextcloud (prázdne = ďalší voľný blok)", - "Base VMID of Paperless (empty = next free block)": "Základné VMID bezpapierové (prázdne = ďalší voľný blok)", + "Base VMID of Paperless (empty = next free block)": "Základné VMID Paperless (prázdne = ďalší voľný blok)", "Base VMID of Tandoor (empty = next free block)": "Základné VMID Tandooru (prázdne = ďalší voľný blok)", "Base VMID of the server (empty = next free block)": "Základné VMID servera (prázdne = ďalší voľný blok)", "Bash prompt path": "Cesta v príkazovom riadku Bash", "Bashrc customization completed": "Úprava bashrc je dokončená", "Basic Settings": "Základné nastavenia", "Basic Utilities": "Základné nástroje", - "Bazarr is a companion application to Sonarr and Radarr. It can manage and download subtitles based on your requirements. You define your preferences by TV show or movie and Bazarr takes care of everything for you.": "Bazarr je sprievodná aplikácia na Sonarr a Radarr. To môže spravovať a stiahnuť titulky na základe requirements. Definujete svoje preferencie pomocou TV show alebo filmu a Bazarr sa postará o všetko pre vás.", - "Beets is a music library manager and not, for the most part, a music player. It does include a simple player plugin and an experimental Web-based player, but it generally leaves actual sound-reproduction to specialized tools.": "Beets je manažérom hudobnej knižnice a väčšinou nie hudobným prehrávačom. To zahŕňa jednoduchý prehrávač plugin a experimentálny webový prehrávač, ale všeobecne ponecháva skutočný zvuk-reprodukciu špecializovaných nástrojov.", + "Bazarr is a companion application to Sonarr and Radarr. It can manage and download subtitles based on your requirements. You define your preferences by TV show or movie and Bazarr takes care of everything for you.": "Bazarr dopĺňa Sonarr a Radarr správou a sťahovaním titulkov. Nastavíte si požiadavky pre seriál alebo film a Bazarr sa postará o zvyšok.", + "Beets is a music library manager and not, for the most part, a music player. It does include a simple player plugin and an experimental Web-based player, but it generally leaves actual sound-reproduction to specialized tools.": "Beets je predovšetkým správca hudobnej knižnice. Obsahuje jednoduchý doplnok na prehrávanie a experimentálny webový prehrávač, samotné prehrávanie však spravidla ponecháva špecializovaným nástrojom.", "Before making any changes, we'll create a safety backup.": "Pred akoukoľvek zmenou vytvoríme bezpečnostnú zálohu.", "Beta (develop branch)": "Beta (vetva develop)", "Beta version:": "Beta verzia:", "Binary not found in extracted content.": "V rozbalenom obsahu sa nenašiel spustiteľný súbor.", "Bind mount added:": "Bind mount pridaný:", "Bind mounts are not included in vzdump": "Pripojené adresáre hostiteľa nie sú zahrnuté v zálohe vzdump", - "Bitcoin Knots can be used as a desktop client for regular payments or as a full node server utility for merchants and other payment services.": "Bitcoin Knots môže byť použitý ako desktop klient pre pravidelné platby alebo ako kompletný uzol server nástroj pre obchodníkov a iné platobné služby.", + "Bitcoin Knots can be used as a desktop client for regular payments or as a full node server utility for merchants and other payment services.": "Bitcoin Knots možno používať ako desktopového klienta na bežné platby alebo ako plnohodnotný serverový uzol pre obchodníkov a platobné služby.", "Blacklist nouveau driver": "Pridá ovládač nouveau na blacklist", "Blacklisting GPU host drivers...": "Pridávam host ovládače GPU na blacklist...", "Blacklisting nouveau driver...": "Pridávam ovládač nouveau na blacklist...", - "Blender is a free and open-source 3D computer graphics software toolset used for creating animated films, visual effects, art, 3D printed models, motion graphics, interactive 3D applications, virtual reality, and computer games. **This image does not support GPU rendering out of the box only accelerated workspace experience**": "Blender je bezplatný a open-source 3D počítačový grafický softvérový súbor používaný na vytváranie animovaných filmov, vizuálnych efektov, umenia, 3D tlačených modelov, pohybovej grafiky, interaktívnych 3D aplikácií, virtuálnej reality a počítačových hier. **Tento obrázok nepodporuje GPU vykresľovanie z krabice iba zrýchlený zážitok z pracovného priestoru", - "Blinko is an AI-powered card note-taking project. Designed for individuals who want to quickly capture and organize their fleeting thoughts. Blinko allows users to seamlessly jot down ideas the moment they strike, ensuring that no spark of creativity is lost.": "Blinko je projekt, ktorý berie na vedomie karty s umelým pohonom. Určené pre jednotlivcov, ktorí chcú quickly zachytiť a organizovať svoje letmé myšlienky. Blinko umožňuje používateľom bezproblémovo zatĺkať nápady v okamihu, keď udrú, takže žiadna iskra tvorivosti nie je stratená.", + "Blender is a free and open-source 3D computer graphics software toolset used for creating animated films, visual effects, art, 3D printed models, motion graphics, interactive 3D applications, virtual reality, and computer games. **This image does not support GPU rendering out of the box only accelerated workspace experience**": "Blender je bezplatný balík s otvoreným zdrojovým kódom na tvorbu 3D grafiky, animovaných filmov, vizuálnych efektov, umenia, modelov na 3D tlač, pohyblivej grafiky, interaktívnych aplikácií, virtuálnej reality a hier. **Tento obraz predvolene nepodporuje vykresľovanie pomocou GPU, iba zrýchlenie pracovného prostredia.**", + "Blinko is an AI-powered card note-taking project. Designed for individuals who want to quickly capture and organize their fleeting thoughts. Blinko allows users to seamlessly jot down ideas the moment they strike, ensuring that no spark of creativity is lost.": "Blinko je kartový poznámkový systém využívajúci AI. Pomáha rýchlo zachytiť a usporiadať myšlienky hneď, ako prídu, aby sa nestratil žiadny tvorivý nápad.", "Blocked GPU ID": "Blokované ID GPU", "Blocked GPU ID for VM Mode": "Blokované ID GPU pre VM režim", "Blocked device(s)": "Blokované zariadenia", @@ -690,7 +693,7 @@ "Browsers & Web Desktops": "Prehliadače a webové plochy", "Build and install the gasket and apex kernel modules (DKMS)": "Zostaví a nainštaluje kernel moduly gasket a apex (DKMS)", "Build dependencies installed.": "Závislosti na zostavenie boli nainštalované.", - "Build your personal knowledge base with TriliumNext Notes": "Budujte si osobné znalosti základne s TriliumNext Poznámky", + "Build your personal knowledge base with TriliumNext Notes": "Vytvorte si osobnú znalostnú databázu s TriliumNext Notes", "Business & ERP": "Biznis a ERP", "CHANGES APPLIED SUCCESSFULLY": "ZMENY BOLI ÚSPEŠNE POUŽITÉ", "CIFS Client Tools: AVAILABLE": "Nástroje CIFS klienta: DOSTUPNÉ", @@ -724,9 +727,9 @@ "CUDA requires a working NVIDIA driver": "CUDA requires pracovný ovládač NVIDIA", "CUDA requires the NVIDIA Container Toolkit on the host": "CUDA requires the NVIDIA Container Toolkit na hostiteľovi", "Calculate all kinds of statistics from your (local) Emby or Jellyfin server": "Vypočítajte všetky druhy štatistík z vášho (miestneho) servera Emby alebo Jellyfin", - "Calibre is a powerful and easy to use e-book manager. Users say it's outstanding and a must-have. It'll allow you to do nearly everything and it takes things a step beyond normal e-book software. It's also completely free and open source and great for both casual users and computer experts.": "Calibre je výkonný a ľahko použiteľný e-kniha manažér. Užívatelia hovoria, že je vynikajúci a musí mať. To vám umožní robiť takmer všetko a to trvá veci krok za normálne e-knihy softvér. Je to tiež úplne zadarmo a otvorený zdroj a skvelé ako pre príležitostných užívateľov a počítačových odborníkov.", + "Calibre is a powerful and easy to use e-book manager. Users say it's outstanding and a must-have. It'll allow you to do nearly everything and it takes things a step beyond normal e-book software. It's also completely free and open source and great for both casual users and computer experts.": "Calibre je výkonný a ľahko použiteľný správca elektronických kníh. Ponúka rozsiahle možnosti nad rámec bežných nástrojov a používatelia ho vysoko hodnotia. Je úplne bezplatný, s otvoreným zdrojovým kódom a vhodný pre bežných používateľov aj odborníkov.", "Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself.": "Calibre-web je webová aplikácia poskytujúca čisté rozhranie pre prehliadanie, čítanie a sťahovanie elektronických kníh pomocou existujúcej databázy Calibre. Je tiež možné integrovať Google disk a upraviť metaúdaje a knižnicu calibre prostredníctvom samotnej aplikácie.", - "Calligra is an office and graphic art suite by KDE. It is available for desktop PCs, tablet computers, and smartphones. It contains applications for word processing, spreadsheets, presentation, vector graphics, and editing databases.": "Calligra je kancelársky a grafický apartmán KDE. Je k dispozícii pre stolové počítače, tabletové počítače a smartphony. Obsahuje aplikácie na spracovanie slov, tabuľky, prezentáciu, vektorovú grafiku a editačné databázy.", + "Calligra is an office and graphic art suite by KDE. It is available for desktop PCs, tablet computers, and smartphones. It contains applications for word processing, spreadsheets, presentation, vector graphics, and editing databases.": "Calligra je kancelársky a grafický balík od KDE pre počítače, tablety a smartfóny. Obsahuje aplikácie na spracovanie textu, tabuľky, prezentácie, vektorovú grafiku a úpravu databáz.", "Cancel": "Zrušiť", "Cancel restore": "Zrušiť obnovu", "Cancel this setup": "Zrušiť toto nastavenie", @@ -783,9 +786,9 @@ "Ceph repository configured for PVE 9": "Repozitár Ceph je nastavený pre PVE 9", "Ceph repository signature verification failed; installation has been stopped": "Overenie podpisu úložiska Ceph zlyhalo;inštalácia bola zastavená", "Ceph version OK:": "Verzia Ceph je v poriadku:", - "Certificate errors are logged in /config/log/letsencrypt inside the container.": "Chyba certifikátu je prihlásená /config/log/letecrypt vo vnútri kontajnera.", + "Certificate errors are logged in /config/log/letsencrypt inside the container.": "Chyby certifikátov sa zaznamenávajú do /config/log/letsencrypt v kontajneri.", "Certificate fingerprint of the PBS server:": "Odtlačok certifikátu PBS servera:", - "Certificate provider: empty for Let's Encrypt, zerossl for ZeroSSL": "Poskytovateľ certifikátu: prázdny pre šifrovanie, nulassl pre ZeroSSL", + "Certificate provider: empty for Let's Encrypt, zerossl for ZeroSSL": "Poskytovateľ certifikátu: prázdne pre Let's Encrypt, zerossl pre ZeroSSL", "Change GPU acceleration?": "Zmeniť zrýchlenie GPU?", "Change Language": "Zmeniť jazyk", "Change Release Channel": "Zmeniť vetvu vydania", @@ -794,7 +797,7 @@ "Change the access network?": "Zmeniť prístupovú sieť?", "Change the recognition now?": "Použiť zvolený CPU/GPU pre rozpoznávanie?", "Change what runs recognition: CPU or GPU": "Vybrať CPU alebo GPU pre rozpoznávanie", - "Changedetection.io provides free, open-source web page monitoring, notification and change detection.": "Changedetection.io poskytuje bezplatné, open-source webové stránky monitorovanie, oznamovanie a detekciu zmien.", + "Changedetection.io provides free, open-source web page monitoring, notification and change detection.": "Changedetection.io poskytuje bezplatné monitorovanie webových stránok, rozpoznávanie zmien a upozornenia s otvoreným zdrojovým kódom.", "Changes applied. A system reboot is recommended for them to take full effect.": "Zmeny boli použité. Odporúča sa reštart systému, aby sa prejavili naplno.", "Changes applied. Data kept.": "Boli použité zmeny. Údaje uchovávané.", "Changes have been applied to the configuration file.": "Zmeny boli zapísané do konfiguračného súboru.", @@ -935,7 +938,7 @@ "Choose what to do with the selected disk:": "Vyberte, čo sa má urobiť s vybraným diskom:", "Choose where to save the backup:": "Vyberte, kam uložiť zálohu:", "Chrome is the official web browser from Google, built to be fast, secure, and customizable.": "Chrome je oficiálny webový prehliadač z Google, postavený tak, aby bol rýchly, bezpečný a prispôsobiteľný.", - "Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all users to experience the web.": "Chromium je open-source prehliadač projekt, ktorého cieľom je vybudovať bezpečnejší, rýchlejší a stabilnejší spôsob, ako pre všetkých užívateľov zažiť web.", + "Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all users to experience the web.": "Chromium je projekt webového prehliadača s otvoreným zdrojovým kódom, ktorého cieľom je bezpečnejšie, rýchlejšie a stabilnejšie používanie webu.", "Circular dependency:": "Okružná závislosť:", "Clean disk metadata": "Vyčistiť metadáta disku", "Cleaned up": "Vyčistená", @@ -960,20 +963,20 @@ "Clearing login credentials...": "Mažem prihlasovacie údaje...", "Client (run a bandwidth test to a server)": "Klient (spustí test priepustnosti voči serveru)", "Client determines best version to use": "Klient si vyberie najvhodnejšiu verziu", - "Clients reach the VPN through the public address and the UDP port given during the installation, so that port must be forwarded to this container.": "Klienti sa dostanú k VPN prostredníctvom verejnej adresy a portu UDP daného počas inštalácie, takže port musí byť odoslaný do tohto kontajnera.", + "Clients reach the VPN through the public address and the UDP port given during the installation, so that port must be forwarded to this container.": "Klienti sa pripájajú k VPN cez verejnú adresu a port UDP zadané počas inštalácie. Tento port musí byť presmerovaný do kontajnera.", "Cloning Coral driver repository (feranick fork)...": "Klonujem repozitár ovládača Coral (feranick fork)...", "Cloning Lynis from GitHub...": "Klonujem Lynis z GitHubu...", "Cloning and applying NVIDIA patch (keylase/nvidia-patch)...": "Klonujem a používam NVIDIA patch (keylase/nvidia-patch)...", "Closed": "Zatvorené", - "Cloud Database Manager.": "Správca databázy cloud.", + "Cloud Database Manager.": "Správca cloudových databáz.", "Cloud storage synchronization and FUSE mounts": "Synchronizácia cloudového úložiska a pripojenia FUSE", "Cloud-Init Automated Installers": "Automatické inštalátory cez Cloud-Init", "Cluster certificates updated": "Certifikáty klastra aktualizované", "Cluster configuration (advanced)": "Nastavenie klastra (pokročilé)", "Cluster data will be applied automatically at next boot.": "Dáta klastra sa použijú automaticky pri ďalšom štarte.", "Cluster upgrade mode": "Režim aktualizácie klastra", - "Code-server is VS Code running on a remote server, accessible through the browser.": "Kód server je VS kód beží na vzdialenom serveri, prístupný cez prehliadač.", - "CodeProject.AI Server": "CodeProject. AI Server", + "Code-server is VS Code running on a remote server, accessible through the browser.": "Code-server spúšťa VS Code na vzdialenom serveri a sprístupňuje ho cez prehliadač.", + "CodeProject.AI Server": "CodeProject.AI Server", "Command": "Príkaz", "Command failed": "Príkaz zlyhal", "Command override for an unknown service:": "Prepísať príkaz pre neznámu službu:", @@ -981,10 +984,10 @@ "Commenting legacy PVE 8 repository .list files (if any)...": "Komentujem staré .list súbory repozitárov PVE 8 (ak existujú)...", "Commenting legacy ceph.list (if present)...": "Komentujem starý ceph.list (ak existuje)...", "Common Issues Check": "Kontrola bežných problémov", - "Common root for the published views": "Spoločný koreň uverejnených názorov", + "Common root for the published views": "Spoločný koreňový adresár sprístupnených pohľadov", "Communication & Community": "Komunikácia a komunita", "Community Scripts": "Komunitné skripty", - "Community single-container Home Assistant OS image": "Jednoduchý kontajner Spoločenstva Home Assistant OS image", + "Community single-container Home Assistant OS image": "Komunitný obraz Home Assistant OS pre jeden kontajner", "Compatibility check": "Kontrola kompatibility", "Compatibility check — OK": "Kontrola kompatibility - OK", "Compatibility check — issues detected": "Kontrola kompatibility - zistené problémy", @@ -999,7 +1002,7 @@ "Complete restore": "Úplná obnova", "Complete the DSM installation wizard": "Dokončite sprievodcu inštaláciou DSM", "Complete the ZimaOS installation wizard": "Dokončite sprievodcu inštaláciou ZimaOS", - "Complete the media server and Seerr accounts, the Bazarr providers and the SABnzbd Usenet credentials when they are selected.": "Dokončiť mediálny server a Seerr účtov, poskytovatelia Bazarr a SABnzbd Usenet credentials, keď sú vybraní.", + "Complete the media server and Seerr accounts, the Bazarr providers and the SABnzbd Usenet credentials when they are selected.": "Ak ste ich vybrali, dokončite účty mediálneho servera a Seerr, poskytovateľov Bazarr a prihlasovacie údaje Usenetu pre SABnzbd.", "Completed": "Dokončené", "Completed Successfully with GPU passthrough configured!": "Úspešne dokončené aj s nastaveným priamym priradením GPU.", "Completed Successfully!": "Úspešne dokončené.", @@ -1062,7 +1065,7 @@ "Configure backup destinations (PBS, Borg, local)": "Nastaviť ciele záloh (PBS, Borg, lokálne)", "Configure the Google Coral APT repository": "Nastaví Google Coral APT repozitár", "Configure the optional variable": "Nakonfigurujte voliteľnú premennú", - "Configure the remote in the Rclone Web UI first. Checking its name may start a stopped CT, even in a dry run. Applying the mount restarts the CT and attempts to publish two FUSE views on the host; a dry run does not publish them.": "Najprv nakonfigurujte diaľkové ovládanie vo webovom používateľskom rozhraní Rclone. Kontrola jeho názvu môže spustiť zastavený CT, dokonca aj pri chode nasucho. Použitie pripojenia reštartuje CT a pokúsi sa publikovať dva pohľady FUSE na hostiteľovi;suchý pokus ich nezverejňuje.", + "Configure the remote in the Rclone Web UI first. Checking its name may start a stopped CT, even in a dry run. Applying the mount restarts the CT and attempts to publish two FUSE views on the host; a dry run does not publish them.": "Najprv nastavte vzdialené úložisko vo webovom rozhraní Rclone. Kontrola jeho názvu môže spustiť zastavený kontajner aj pri skúšobnom behu. Použitie pripojenia kontajner reštartuje a pokúsi sa sprístupniť dva pohľady FUSE na hostiteľovi. Skúšobný beh tieto pohľady nesprístupňuje.", "Configure with username and password": "Nastaviť meno a heslo", "Configured Ports": "Nastavené porty", "Configured but doesn't exist": "Je nastavené, ale neexistuje", @@ -1272,7 +1275,7 @@ "Converting the container to privileged...": "Konverzia kontajnera na privilegovaný...", "Converts to deb822; keeps .list backups as .bak": "Prevedie na deb822; zálohy .list ponechá ako .bak", "Coordinated backups require zstd": "Koordinované zálohy require zstd", - "Cops by Sébastien Lucas, now maintained by MikesPub, stands for Calibre OPDS (and HTML) Php Server.": "Cops by Sébastien Lucas, teraz udržiavané MikesPub, znamená Calibre OPDS (a HTML) Php Server.", + "Cops by Sébastien Lucas, now maintained by MikesPub, stands for Calibre OPDS (and HTML) Php Server.": "COPS od Sébastiena Lucasa, dnes spravovaný MikesPub, znamená Calibre OPDS (a HTML) PHP Server.", "Copy a peer configuration to the host with: pct pull /config/peer1/peer1.conf peer1.conf": "Kopírovať konfiguráciu peer k hostiteľovi s: pct pull /config/peer1/peer1.conf peer1.conf", "Copy failed": "Kopírovanie zlyhalo", "Copy that file offsite yourself, or download it from the Monitor.": "Skopírujte si tento súbor mimo servera sami alebo ho stiahnite z Monitoru.", @@ -1290,7 +1293,7 @@ "Coral M.2 Apex detected, configuring...": "Našiel sa Coral M.2 Apex, nastavujem...", "Coral PCIe/M.2": "Coral PCIe/M.2", "Coral PCIe/M.2 device node": "Uzol zariadenia Coral PCIe/M.2", - "Coral PCIe/M.2 node (e.g. /dev/apex_0)": "Coral PCIe/M.2 uzly (napr. /dev/apex_0)", + "Coral PCIe/M.2 node (e.g. /dev/apex_0)": "Uzol zariadenia Coral PCIe/M.2 (napr. /dev/apex_0)", "Coral TPU Installation": "Inštalácia Coral TPU", "Coral TPU Uninstall": "Odinštalovanie Coral TPU", "Coral TPU device nodes detected with correct group (apex).": "Uzly zariadenia Coral TPU sa našli so správnou skupinou (apex).", @@ -1518,8 +1521,8 @@ "Cross-host restore: guest IDs in backup overlap live IDs on target:": "Obnova medzi hostami: ID hostí zo zálohy sa prekrývajú so živými ID na cieli:", "Cross-kernel restore — kernel-tied paths merged, not copied": "Obnova medzi kernelmi - cesty viazané na kernel boli zlúčené, nie skopírované", "Cross-kernel — paths hidden from picker": "Rozdielny kernel - cesty skryté vo výbere", - "Cross-platform file sharing made easy.": "Cross-platform súboru zdieľanie jednoduché.", - "Cross-platform monitoring tool.": "Monitorovací nástroj s viacerými platformami.", + "Cross-platform file sharing made easy.": "Jednoduché zdieľanie súborov medzi platformami.", + "Cross-platform monitoring tool.": "Multiplatformový monitorovací nástroj.", "Cross-version detected — safe restore mode": "Zistená rozdielna verzia - bezpečný režim obnovy", "Current": "Aktuálne", "Current CIFS mounts:": "Aktuálne CIFS pripojenia:", @@ -1591,7 +1594,7 @@ "Customization": "Prispôsobenie", "Customize bashrc": "Prispôsobiť bashrc", "Customizing bashrc for root user...": "Upravujem bashrc pre používateľa root...", - "DB Browser for SQLite is a high quality, visual, open source tool to create, design, and edit database files compatible with SQLite.": "DB prehliadač pre SQLite je vysoko kvalitný, vizuálny, open source nástroj na vytváranie, navrhovanie a úpravu databázových súborov kompatibilných so SQLite.", + "DB Browser for SQLite is a high quality, visual, open source tool to create, design, and edit database files compatible with SQLite.": "DB Browser for SQLite je kvalitný vizuálny nástroj s otvoreným zdrojovým kódom na vytváranie, navrhovanie a úpravu databázových súborov SQLite.", "DHCP (automatic)": "DHCP (automatické)", "DISABLED unless you enable it": "VYPNUTÉ, kým to nezapnete", "DKMS add failed. Check": "Pridanie do DKMS zlyhalo. Skontrolujte", @@ -1605,11 +1608,11 @@ "DKMS registrations removed.": "registrácie DKMS boli odstránené.", "DNS Resolution": "Preklad DNS", "DNS lookup for a domain": "DNS vyhľadanie domény", - "DNS plugin used with dns validation (cloudflare, duckdns, ovh...)": "DNS plugin použitý s dns validácia (cloudflare, duckdns, ovh...)", + "DNS plugin used with dns validation (cloudflare, duckdns, ovh...)": "Doplnok DNS na overenie cez dns (cloudflare, duckdns, ovh...)", "DNS server written in the client configurations": "DNS server napísaný v klientskych konfiguráciách", - "DNS server written in the peer configurations (auto or an IP address)": "DNS server napísaný v peer konfiguráciách (auto alebo IP adresa)", - "DOGWALK is Blender Studio's long awaited second game project, focused on creating a bite-sized interactive storytelling playground. Play as a big adorable dog and explore the winter woods with a little kid.": "DOGWALK je Blender Štúdio dlho očakávaný druhý herný projekt, zameraný na vytvorenie uhryznutie-veľké interaktívne rozprávanie príbehov ihrisko. Hrať ako veľký rozkošný pes a preskúmať zimné lesy s malým dieťaťom.", - "DOSBox Staging is a modern continuation of DOSBox a free and open-source emulator that enables the execution of MS-DOS software, especially video games.": "DOSBox Staging je moderné pokračovanie DOSBoxu zadarmo a open-source emulátor, ktorý umožňuje realizáciu MS-DOS softvéru, najmä videohry.", + "DNS server written in the peer configurations (auto or an IP address)": "Server DNS zapisovaný do nastavení účastníkov (auto alebo IP adresa)", + "DOGWALK is Blender Studio's long awaited second game project, focused on creating a bite-sized interactive storytelling playground. Play as a big adorable dog and explore the winter woods with a little kid.": "DOGWALK je druhý herný projekt Blender Studio s krátkym interaktívnym príbehom. Hrajte za veľkého roztomilého psa a skúmajte zimný les s malým dieťaťom.", + "DOSBox Staging is a modern continuation of DOSBox a free and open-source emulator that enables the execution of MS-DOS software, especially video games.": "DOSBox Staging je moderným pokračovaním DOSBoxu, bezplatného emulátora s otvoreným zdrojovým kódom na spúšťanie softvéru MS-DOS, najmä hier.", "DVB device directory": "Adresár zariadení DVB", "Daily at 02:00": "Denne o 02:00", "Daily at 03:00": "Denne o 03:00", @@ -1628,8 +1631,8 @@ "Database type (sqlite, mysql or pgsql)": "Typ databázy (sqlite, mysql alebo pgsql)", "Databases": "Databázy", "Datastore name:": "Názov datastore:", - "Davos is an FTP automation tool that periodically scans given host locations for new files. It can be configured for various purposes, including listening for specific files to appear in the host location, ready for it to download and then move, if required. It also supports completion notifications as well as downstream API calls, to further the workflow.": "Davos je nástroj na automatizáciu FTP, ktorý pravidelne skenuje ubytovacie miesta pre nové súbory. To môže byť nakonfigurovaný pre rôzne účely, vrátane počúvania pre konkrétne súbory sa objaví v mieste hostiteľa, pripravený na stiahnutie a potom presunúť, ak required. Podporuje aj oznámenia o dokončení, ako aj následné výzvy týkajúce sa API na podporu pracovného postupu.", - "Ddclient is a Perl client used to update dynamic DNS entries for accounts on Dynamic DNS Network Service Provider. It was originally written by Paul Burry and is now mostly by wimpunk. It has the capability to update more than just dyndns and it can fetch your WAN-ipaddress in a few different ways.": "Ddclient je Perl klient používaný na aktualizáciu dynamických položiek DNS pre účty na Dynamic DNS Network Service Provider. Pôvodne bol napísaný Paulom Burrym a teraz je väčšinou zbabelý. Má schopnosť aktualizovať viac ako len dyndns a môže priniesť WAN-ipaddress niekoľkými rôznymi spôsobmi.", + "Davos is an FTP automation tool that periodically scans given host locations for new files. It can be configured for various purposes, including listening for specific files to appear in the host location, ready for it to download and then move, if required. It also supports completion notifications as well as downstream API calls, to further the workflow.": "Davos automatizuje FTP prenosy pravidelným vyhľadávaním nových súborov na určených miestach servera. Vie čakať na konkrétne súbory, stiahnuť ich a podľa potreby presunúť. Podporuje upozornenia po dokončení aj následné volania API.", + "Ddclient is a Perl client used to update dynamic DNS entries for accounts on Dynamic DNS Network Service Provider. It was originally written by Paul Burry and is now mostly by wimpunk. It has the capability to update more than just dyndns and it can fetch your WAN-ipaddress in a few different ways.": "Ddclient je klient v Perle na aktualizáciu záznamov dynamického DNS u poskytovateľov tejto služby. Pôvodne ho napísal Paul Burry, dnes ho spravuje najmä wimpunk. Podporuje aj iné služby než dyndns a verejnú IP adresu siete WAN zisťuje viacerými spôsobmi.", "Deactivate Monitor": "Vypnúť Monitor", "Deactivate ProxMenux Monitor": "Vypnúť ProxMenux Monitor", "Debian repositories missing; creating default source file": "Debian repozitáre chýbajú; vytváram predvolený zdrojový súbor", @@ -1637,7 +1640,7 @@ "Decryption failed. The passphrase may be wrong, or the blob is corrupt. Try again?": "Dešifrovanie zlyhalo. Fráza môže byť nesprávna alebo je balíček poškodený. Skúsiť znova?", "Dedicated container volume (included in backups)": "Samostatný dátový zväzok kontajnera (zahrnutý v zálohách)", "Dedicated container volumes (included in backups)": "Samostatné dátové zväzky kontajnerov (zahrnuté v zálohách)", - "DeepSeek Harness “Everything is a Plugin“.": "DeepSeek Harness, všetko je Plugin.", + "DeepSeek Harness “Everything is a Plugin“.": "DeepSeek Harness – všetko je doplnok.", "Default ACLs applied for group inheritance.": "Predvolené ACL boli použité pre dedenie skupiny.", "Default Credentials": "Predvolené prihlasovacie údaje", "Default Gateway": "Predvolená brána", @@ -1667,11 +1670,11 @@ "Delete it?": "Vymazať úložisko?", "Delete job": "Vymazať úlohu", "Delete scheduled backup job?": "Vymazať naplánovanú úlohu zálohy?", - "Delete the image to free the space?": "Odstrániť obrázok na voľné miesto?", - "Delete the images to free the space?": "Odstrániť obrázky na voľné miesto?", + "Delete the image to free the space?": "Odstrániť obraz a uvoľniť miesto?", + "Delete the images to free the space?": "Odstrániť obrazy a uvoľniť miesto?", "Delete this corrupt archive and pick another": "Vymazať tento poškodený archív a vybrať iný", "Deleting job...": "Odstraňuje sa úloha...", - "Deluge is a lightweight, Free Software, cross-platform BitTorrent client.": "Deluge je ľahký, Free Software, cross-platform BitTorrent klient.", + "Deluge is a lightweight, Free Software, cross-platform BitTorrent client.": "Deluge je nenáročný multiplatformový klient BitTorrent so slobodnou licenciou.", "Dependencies installed successfully": "Závislosti boli úspešne nainštalované", "Deploy with this configuration?": "Nasadiť s týmto nastavením?", "Deploying Secure Gateway...": "Nasadzuje sa Secure Gateway...", @@ -1760,8 +1763,8 @@ "Directory does not exist and was not created.": "Priečinok neexistuje a nebol vytvorený.", "Directory does not exist:": "Priečinok neexistuje:", "Directory error": "Chyba priečinka", - "Directory for the read-only view": "Adresár pre zobrazenie len na čítanie", - "Directory for the read/write view": "Adresár pre zobrazenie čítania/písania", + "Directory for the read-only view": "Adresár pohľadu iba na čítanie", + "Directory for the read/write view": "Adresár pohľadu na čítanie a zápis", "Directory not found": "Priečinok sa nenašiel", "Directory storage added successfully to Proxmox!": "Priečinkové úložisko bolo úspešne pridané do Proxmoxu!", "Directory successfully.": "Priečinok je pripravený.", @@ -1834,7 +1837,7 @@ "Display volume groups (LVM)": "Zobraziť skupiny zväzkov (LVM)", "Do not assume port 8123 remains active after onboarding on Home Assistant Core 2026.8 or newer.": "Nepredpokladajte, že port 8123 zostane aktívny po nalodení na Home Assistant Core 2026.8 alebo novšie.", "Do not claim in-place OCI image updates are validated until rootfs replacement and rollback have been tested without losing the managed /mnt/data volume.": "Nevyhlasujte, že aktualizácie obrazu OCI sú validované na mieste, kým sa neotestujú rootfs náhrada a rollback bez straty riadeného /mnt/data objemu.", - "Do not enable automatically on unsupported AMD integrated GPUs. HSA overrides are manual compatibility experiments, not a validated default.": "Nepovoľovať automaticky na nepodporovaných integrovaných GPU AMD. HSA sú manuálne kompatibilné experimenty, nie validované predvolené.", + "Do not enable automatically on unsupported AMD integrated GPUs. HSA overrides are manual compatibility experiments, not a validated default.": "Nepovoľujte automaticky na nepodporovaných integrovaných GPU AMD. Prepísanie nastavení HSA je ručný experiment s kompatibilitou, nie overené predvolené nastavenie.", "Do not mount": "Nepripojiť", "Do not run the upgrade from the Web UI virtual console (it will disconnect)": "Nespúšťajte aktualizáciu z virtuálnej konzoly vo webovom rozhraní (odpojí sa)", "Do not start the VM until the system has been rebooted.": "VM nespúšťajte, kým sa systém nereštartuje.", @@ -1885,20 +1888,20 @@ "Docker/CLI not available yet or no valid answer": "Docker/CLI ešte nie je k dispozícii alebo nie je platná odpoveď", "Documents & Notes": "Dokumenty a poznámky", "Documents volume size in GB": "Veľkosť dátového zväzku pre dokumenty v GB", - "Dokuwiki is a simple to use and highly versatile Open Source wiki software that doesn't require a database. It is loved by users for its clean and readable syntax. The ease of maintenance, backup and integration makes it an administrator's favorite. Built in access controls and authentication connectors make DokuWiki especially useful in the enterprise context and the large number of plugins contributed by its vibrant community allow for a broad range of use cases beyond a traditional wiki.": "Dokuwiki je jednoduché použitie a vysoko univerzálny softvér Open Source wiki, ktorý nerequire databázu. Používatelia ho milujú pre jeho čistú a čitateľnú syntax. Jednoduchosť údržby, zálohovania a integrácie z neho robí administrátora obľúbeným. Postavený v prístupových a autentifikačných konektorov, aby DokuWiki obzvlášť užitočné v podnikovom kontexte a veľký počet pluginov, ktoré prispeli jeho pulzujúce komunity umožňujú širokú škálu prípadov použitia mimo tradičné wiki.", - "Dolphin Emulator lets you play GameCube and Wii games with various graphical enhancements and other features are available to improve your game experience.": "Dolphin Emulator vám umožní hrať hryCube a Wii hry s rôznymi grafickými vylepšeniami a ďalšie funkcie sú k dispozícii pre zlepšenie herné skúsenosti.", - "Domain for the certificate (example.com)": "Doména pre osvedčenie (príklad.com)", - "Doplarr is an *arr request bot for Discord.\"": "Doplarr je robot pre Discord.", - "Doplarr_rs is a Discord bot for requesting media through *arr backends, written in Rust.": "Doplarr_rs je robot Discord pre žiadanie médií cez arr backends, napísané v Rust.", - "Double Commander is a free cross platform open source file manager with two panels side by side. It is inspired by Total Commander and features some new ideas.": "Double Commander je free cross platform open source správca súborov s dvoma panelmi vedľa seba. Je inšpirovaný Total Commander a obsahuje niektoré nové nápady.", - "Download Spotify music with album art and metadata": "Stiahnuť Spotify hudbu s albumom a metadátami", + "Dokuwiki is a simple to use and highly versatile Open Source wiki software that doesn't require a database. It is loved by users for its clean and readable syntax. The ease of maintenance, backup and integration makes it an administrator's favorite. Built in access controls and authentication connectors make DokuWiki especially useful in the enterprise context and the large number of plugins contributed by its vibrant community allow for a broad range of use cases beyond a traditional wiki.": "DokuWiki je jednoduchý a všestranný wiki systém s otvoreným zdrojovým kódom, ktorý nevyžaduje databázu. Ponúka prehľadnú syntax a jednoduchú údržbu, zálohovanie aj integráciu. Vstavané riadenie prístupu a možnosti overovania sú vhodné aj pre firmy. Množstvo komunitných doplnkov rozširuje použitie nad rámec tradičnej wiki.", + "Dolphin Emulator lets you play GameCube and Wii games with various graphical enhancements and other features are available to improve your game experience.": "Dolphin Emulator umožňuje hrať hry GameCube a Wii s grafickými vylepšeniami a ďalšími funkciami na zlepšenie herného zážitku.", + "Domain for the certificate (example.com)": "Doména pre certifikát (example.com)", + "Doplarr is an *arr request bot for Discord.\"": "Doplarr je robot pre Discord na zadávanie požiadaviek aplikáciám *arr.", + "Doplarr_rs is a Discord bot for requesting media through *arr backends, written in Rust.": "Doplarr_rs je robot pre Discord napísaný v Ruste na zadávanie požiadaviek na médiá cez aplikácie *arr.", + "Double Commander is a free cross platform open source file manager with two panels side by side. It is inspired by Total Commander and features some new ideas.": "Double Commander je bezplatný multiplatformový správca súborov s otvoreným zdrojovým kódom a dvoma panelmi vedľa seba. Inšpiruje sa Total Commanderom a pridáva vlastné funkcie.", + "Download Spotify music with album art and metadata": "Sťahovanie hudby zo Spotify s obalmi albumov a metadátami", "Download an image to template storage": "Stiahnuť obraz do úložiska šablón", "Download an oci-compose from an address": "Stiahnuť oci-compose z adresy", "Download failed for all attempted URLs": "Sťahovanie zlyhalo zo všetkých vyskúšaných adries", "Download its Compose file from an address": "Stiahnuť súbor Compose z URL adresy", "Download keyfile": "Stiahnuť keyfile", "Download latest VirtIO ISO automatically": "Stiahnuť najnovšie VirtIO ISO automaticky", - "Downloaded OCI images deleted:": "Stiahnuté obrázky OCI odstránené:", + "Downloaded OCI images deleted:": "Odstránené stiahnuté obrazy OCI:", "Downloaded amdgpu_top": "amdgpu_top bol stiahnutý", "Downloading": "Sťahujem", "Downloading Helper-Scripts logo...": "Sťahujem logo Helper-Scripts...", @@ -1923,11 +1926,11 @@ "Dry run completed; no changes were made.": "Suchý beh dokončený; neboli vykonané žiadne zmeny.", "Dry run completed; no containers were created.": "Suchý beh dokončený; neboli vytvorené žiadne kontajnery.", "Dry run completed; the container and the mounts were not changed.": "Suchý chod dokončený; kontajner a držiaky neboli zmenené.", - "DuckDNS subdomain without .duckdns.org (comma separated for several)": "DuckDNS subdoména bez .duckdns.org (koma oddelené pre niekoľko)", + "DuckDNS subdomain without .duckdns.org (comma separated for several)": "Subdoména DuckDNS bez .duckdns.org (viac subdomén oddeľte čiarkami)", "DuckDNS token from your account at duckdns.org": "DuckDNS token z vášho účtu na duckdns.org", "DuckDNS updates the subdomain every 5 minutes. Without UPDATE_IP, DuckDNS itself detects the public IPv4 address of the request.": "DuckDNS aktualizuje subdoménu každých 5 minút. Bez UPDATE_IP samotná DuckDNS detekuje verejnú IPv4 adresu žiadosti.", - "DuckStation is a PS1 Emulator aiming for the best accuracy and game support.": "DuckStation je PS1 Emulátor zameraný na najlepšie accuracy a hernú podporu.", - "Duckdns is a free service which will point a DNS (sub domains of duckdns.org) to an IP of your choice. The service is completely free, and doesn't require reactivation or forum posts to maintain its existence.": "Duckdns je bezplatná služba, ktorá bude ukazovať DNS (pod domény duckdns.org) na IP podľa vášho výberu. Služba je úplne zadarmo, a nie je require reaktivácia alebo fóra príspevky udržať jeho existenciu.", + "DuckStation is a PS1 Emulator aiming for the best accuracy and game support.": "DuckStation je emulátor PS1 zameraný na čo najpresnejšiu emuláciu a podporu hier.", + "Duckdns is a free service which will point a DNS (sub domains of duckdns.org) to an IP of your choice. The service is completely free, and doesn't require reactivation or forum posts to maintain its existence.": "DuckDNS je bezplatná služba smerujúca subdomény duckdns.org na zvolenú IP adresu. Nevyžaduje opätovnú aktiváciu ani príspevky vo fóre na zachovanie účtu.", "Dumping AMD GPU ROM BIOS via sysfs...": "Vytváram dump AMD GPU ROM BIOS cez sysfs...", "Duplicate IP addresses found": "Našli sa duplicitné IP adresy", "Duplicate parameters cleaned": "Duplicitné parametre sú vyčistené", @@ -1949,31 +1952,31 @@ "EMERGENCY PROXMOX SYSTEM REPAIR": "NÚDZOVÁ OPRAVA SYSTÉMU PROXMOX", "ENABLED for restore": "ZAPNUTÉ pre obnovu", "EXISTS": "EXISTUJE", - "Each /request command needs a backend: add a [[backends]] block in the same file with the url and api_key of your Sonarr, Radarr or Seerr instance, then restart the container.": "Každý príkaz / žiadosť potrebuje backend: pridať [[[backends]]] blok v rovnakom súbore s url a api key vášho Sonarr, Radarr alebo Seerr inštancie, potom reštartujte kontajner.", + "Each /request command needs a backend: add a [[backends]] block in the same file with the url and api_key of your Sonarr, Radarr or Seerr instance, then restart the container.": "Každý príkaz /request vyžaduje backend. Do rovnakého súboru pridajte blok [[backends]] s url a api_key vašej inštancie Sonarr, Radarr alebo Seerr a kontajner reštartujte.", "Each LUN will appear as a block device assignable to VMs.": "Každý LUN sa zobrazí ako blokové zariadenie, ktoré sa dá priradiť k VM.", - "Each peer gets its configuration and its QR code inside the container: /config/peer1/peer1.conf and /config/peer1/peer1.png, or /config/peer_/peer_.conf when names were given.": "Každý peer dostane svoju konfiguráciu a QR kód vnútri kontajnera: /config/peer1/peer1.conf a /config/peer1/peer1.png, alebo /config/peer /peer .conf pri uvedení mien.", - "Ebook and audiobook collection manager for Usenet and BitTorrent users.": "Ebook a audioknihy správca zberu pre užívateľov Usenet a BitTorrent.", - "Eden is an experimental open-source emulator for the Nintendo Switch, built with performance and stability in mind.": "Eden je experimentálny open-source emulátor pre Nintendo Switch, postavený s výkonom a stabilitou na mysli.", + "Each peer gets its configuration and its QR code inside the container: /config/peer1/peer1.conf and /config/peer1/peer1.png, or /config/peer_/peer_.conf when names were given.": "Každý účastník má v kontajneri konfiguráciu a QR kód: /config/peer1/peer1.conf a /config/peer1/peer1.png alebo /config/peer_/peer_.conf, ak boli zadané mená.", + "Ebook and audiobook collection manager for Usenet and BitTorrent users.": "Správca zbierky elektronických kníh a audiokníh pre používateľov Usenetu a BitTorrentu.", + "Eden is an experimental open-source emulator for the Nintendo Switch, built with performance and stability in mind.": "Eden je experimentálny emulátor Nintendo Switch s otvoreným zdrojovým kódom a dôrazom na výkon a stabilitu.", "Edge TPU runtime installed.": "Edge TPU runtime bol nainštalovaný.", "Edit": "Upraviť", "Edit Job": "Upraviť úlohu", "Edit raw CT configuration file": "Upraviť konfiguračný súbor CT", "Edit raw VM configuration file": "Upraviť konfiguračný súbor VM", "Edit the VM machine type to q35 and try again.": "Zmeňte typ stroja VM na q35 a skúste to znova.", - "Email address for certificate expiry notices (required by ZeroSSL)": "E-mailová adresa pre oznámenia o uplynutí platnosti certifikátu (required by ZeroSSL)", + "Email address for certificate expiry notices (required by ZeroSSL)": "E-mail na upozornenia pred vypršaním certifikátu (vyžaduje ZeroSSL)", "Email address of the LibreDB Studio administrator": "E-mailová adresa správcu LibreDB Studio", "Email address of the NetBox admin account": "E-mailová adresa admin účtu NetBox", "Email address of the admin account": "E-mailová adresa účtu správcu", "Email address of the superuser account": "E-mailová adresa účtu superužívateľa", "Emby WebUI": "Emby WebUI", - "Emby organizes video, music, live TV, and photos from personal media libraries and streams them to smart TVs, streaming boxes and mobile devices. This container is packaged as a standalone emby Media Server.": "Emby organizuje video, hudbu, živú televíziu a fotografie z osobných mediálnych knižníc a prenáša ich do inteligentných televízorov, streamovacích skríň a mobilných zariadení. Tento kontajner je balený ako samostatný emby Media Server.", + "Emby organizes video, music, live TV, and photos from personal media libraries and streams them to smart TVs, streaming boxes and mobile devices. This container is packaged as a standalone emby Media Server.": "Emby usporadúva videá, hudbu, živú televíziu a fotografie z osobných knižníc a streamuje ich do inteligentných televízorov, prehrávačov a mobilných zariadení. Tento kontajner obsahuje samostatný Emby Media Server.", "Emergency Proxmox System Repair": "Núdzová oprava systému Proxmox", "Emergency recovery:": "Núdzová obnova:", "Empowering the smart home": "Posilnenie inteligentného domova", "Empty": "Prázdny", "Empty exec service check": "Prázdna kontrola exec služby", "Empty or duplicated NVIDIA identity": "Prázdna alebo duplikovaná identita NVIDIA", - "EmulatorJS is a Docker-based emulator application that can simulate various operating systems and device environments within containers for development, testing, and learning purposes.": "EmulatorJS je aplikácia Docker založená na emulátore, ktorá simuluje rôzne operatingové systémy a prostredia zariadení v kontajneroch pre vývoj, testovanie a učenie.", + "EmulatorJS is a Docker-based emulator application that can simulate various operating systems and device environments within containers for development, testing, and learning purposes.": "EmulatorJS je emulačná aplikácia založená na Dockeri, ktorá simuluje rôzne operačné systémy a prostredia zariadení v kontajneroch na vývoj, testovanie a vzdelávanie.", "Enable": "Zapnúť", "Enable / disable job timer": "Zapnúť / vypnúť timer úlohy", "Enable High Availability services": "Zapnúť služby High Availability", @@ -1981,24 +1984,24 @@ "Enable IOMMU support if not enabled": "Zapne podporu IOMMU, ak ešte nie je zapnutá", "Enable IOMMU, reboot the host, and try again.": "Zapnite IOMMU, reštartujte server a skúste to znova.", "Enable Intel/AMD VA-API video acceleration": "Povoliť video zrýchlenie Intel/AMD VA-API", - "Enable NVIDIA transcoding and HDR10/Dolby Vision to SDR tone mapping": "Povoliť transkódovanie NVIDIA a HDR10/Dolby Vision do mapovania SDR tónu", + "Enable NVIDIA transcoding and HDR10/Dolby Vision to SDR tone mapping": "Povoliť prevod videa cez NVIDIA a mapovanie tónov HDR10/Dolby Vision na SDR", "Enable Remote Desktop (RDP) before disabling the virtual display.": "Pred vypnutím virtuálneho zobrazenia zapnite Vzdialenú plochu (RDP).", "Enable SSD emulation for this disk?": "Zapnúť pre tento disk emuláciu SSD?", "Enable TCP BBR/Fast Open control": "Zapnúť TCP BBR/Fast Open", - "Enable VA-API transcoding and HDR10/Dolby Vision to SDR tone mapping": "Povoliť transkódovanie VA-API a HDR10/Dolby Vision do mapovania tónu SDR", - "Enable VA-API, hardware encoding and OpenCL tone mapping in Jellyfin": "Povoliť VA-API, kódovanie hardvéru a mapovanie OpenCL tónu v Jellyfin", + "Enable VA-API transcoding and HDR10/Dolby Vision to SDR tone mapping": "Povoliť prevod videa cez VA-API a mapovanie tónov HDR10/Dolby Vision na SDR", + "Enable VA-API, hardware encoding and OpenCL tone mapping in Jellyfin": "Povoliť VA-API, hardvérové kódovanie a mapovanie tónov pomocou OpenCL v Jellyfine", "Enable VFIO IOMMU support": "Zapnúť podporu VFIO/IOMMU", "Enable ZFS autotrim (SSD/NVMe pools)": "Zapnúť ZFS autotrim (SSD/NVMe pooly)", "Enable ZRAM swap profile": "Povoliť výmenný profil ZRAM", - "Enable a mount on an existing Rclone OCI container": "Povoliť montáž na existujúci Rclone OCI kontajner", - "Enable an optical drive for MakeMKV": "Povoliť optický pohon pre MakeMKV", + "Enable a mount on an existing Rclone OCI container": "Nastaviť pripojenie na existujúcom OCI kontajneri Rclone", + "Enable an optical drive for MakeMKV": "Sprístupniť optickú mechaniku pre MakeMKV", "Enable auto-sync if /var/log exceeds 90% of its size?": "Zapnúť automatickú synchronizáciu, keď /var/log presiahne 90 % svojej veľkosti?", "Enable fast reboots": "Zapnúť rýchle reštarty", "Enable job": "Povoliť úlohu", "Enable restart on kernel panic": "Zapnúť reštart pri kernel panic", - "Enable the NVIDIA GPU requested by the image": "Povoliť NVIDIA GPU požadovanú obrázkom", - "Enable the NVIDIA GPU required by Open WebUI CUDA": "Povoliť NVIDIA GPU required by Open WebUI CUDA", - "Enable this FUSE mount now and restart the CT?": "Povoliť túto FUSE pripojiť teraz a reštartovať CT?", + "Enable the NVIDIA GPU requested by the image": "Povoliť GPU NVIDIA požadované obrazom", + "Enable the NVIDIA GPU required by Open WebUI CUDA": "Povoliť GPU NVIDIA vyžadované aplikáciou Open WebUI CUDA", + "Enable this FUSE mount now and restart the CT?": "Povoliť toto pripojenie FUSE teraz a reštartovať kontajner?", "Enable/Disable job": "Zapnúť/vypnúť úlohu", "Enabled": "Zapnuté", "Enabled (device pending — load apex module or reboot)": "Zapnuté (zariadenie čaká - načítajte modul apex alebo reštartujte)", @@ -2145,8 +2148,8 @@ "Enterprise repository returned 401 Unauthorized (no valid subscription). Switch to the no-subscription repository and retry?": "Enterprise repozitár vrátil 401 Unauthorized (žiadne platné predplatné). Prepnúť na no-subscription repozitár a skúsiť znova?", "Enterprise repository unauthorized and fallback declined by user": "Enterprise repozitár nie je autorizovaný a používateľ odmietol náhradné riešenie", "Entropy generation optimization removed": "Optimalizácia generovania entropie bola odstránená", - "Environment entry without an explicit value": "Vstup do životného prostredia bez explicitnej hodnoty", - "Environment override for an unknown service:": "Prostredie prepísať pre neznámu službu:", + "Environment entry without an explicit value": "Položka premennej prostredia bez výslovne zadanej hodnoty", + "Environment override for an unknown service:": "Prepísanie premenných prostredia pre neznámu službu:", "Equivalent manual flow of disk_host.sh: partition, format, mount, persist, register in Proxmox.": "Rovnaký ručný postup ako v disk_host.sh: rozdeliť, formátovať, pripojiť, uložiť natrvalo a zaregistrovať v Proxmoxe.", "Equivalent manual flow of iscsi_host.sh.": "Rovnaký ručný postup ako v iscsi_host.sh.", "Equivalent manual flow used by Local Shared Manager.": "Rovnaký ručný postup používa aj správca lokálneho zdieľania.", @@ -2173,7 +2176,7 @@ "Every node of this cluster already has this file. If you restore this container on any other Proxmox host, copy it to the same path first, because the container backup does not include it:": "Každý uzol tohto klastra už má tento súbor. Ak obnovíte tento kontajner na akomkoľvek inom hostiteľovi Proxmox, najskôr ho skopírujte do rovnakej cesty, pretože záloha kontajnera ho neobsahuje:", "Every path in this backup is kernel-tied: the restore applies these paths automatically via the safe-subset filter and re-merges the operator's tuning.": "Každá cesta v tejto zálohe je viazaná na kernel: obnova tieto cesty použije automaticky cez filter bezpečnej podmnožiny a znovu zlúči vlastné ladenie správcu.", "Everything restorable in this backup will be restored": "Všetko obnoviteľné z tejto zálohy sa obnoví", - "Exact name of the remote": "Presný názov ovládača", + "Exact name of the remote": "Presný názov vzdialeného úložiska", "Example output: rootfs: local-lvm:vm-114-disk-0,size=8G": "Príklad výstupu: rootfs: local-lvm:vm-114-disk-0,size=8G", "Example target: /dev/sdb": "Príklad cieľa: /dev/sdb", "Example value:": "Príklad hodnoty:", @@ -2194,7 +2197,7 @@ "Execute destructive rollback?": "Vykonať deštruktívny rollback?", "Executing destructive rollback (operator confirmed) ...": "Vykonávam deštruktívny rollback (správca potvrdil) ...", "Executing:": "Spúšťam:", - "Execution engine for Index-TTS": "Vykonávací motor pre Index-TTS", + "Execution engine for Index-TTS": "Výpočtový engine pre Index-TTS", "Existing Groups": "Existujúce skupiny", "Existing TLS certificate reused:": "Existujúce osvedčenie TLS opätovne použité:", "Existing file, re-downloading...": "Súbor už existuje, sťahujem ho znova...", @@ -2241,7 +2244,7 @@ "Extended Filesystem 4 (recommended)": "Extended Filesystem 4 (odporúčané)", "External ZFS ARC settings restored:": "Externé nastavenia ZFS ARC boli obnovené:", "External ZFS configuration changed after the ProxMenux migration; current file and backup preserved:": "Externá konfigurácia ZFS sa po migrácii ProxMenux zmenila; aktuálny súbor aj záloha zostali zachované:", - "External credential is empty or spans multiple lines": "Externý credential je prázdny alebo rozpätie viacerých liniek", + "External credential is empty or spans multiple lines": "Externý prihlasovací údaj je prázdny alebo obsahuje viac riadkov", "External disk for backup": "Externý disk na zálohu", "External field not reserved:": "Vonkajšie pole nie je vyhradené:", "Extra parameters for minisatip (optional)": "Ďalšie parametre pre minisatip (voliteľné)", @@ -2257,9 +2260,9 @@ "Extraction failed. Check log:": "Rozbalenie zlyhalo. Skontrolujte záznam:", "Extraction successful": "Rozbalenie bolo úspešné", "FAILED": "ZLYHALO", - "FFmpeg version the node uses (7 by default)": "FFmpeg verzia uzol používa (7 predvolene)", + "FFmpeg version the node uses (7 by default)": "Verzia FFmpeg používaná uzlom (predvolene 7)", "FINAL CONFIRMATION — DATA WILL BE ERASED": "POSLEDNÉ POTVRDENIE — DÁTA SA VYMAŽÚ", - "FIleZilla Client is a fast and reliable cross-platform FTP, FTPS and SFTP client with lots of useful features and an intuitive graphical user interface.": "FIleZilla Client je rýchly a spoľahlivý cross-platform FTP, FTPS a SFTP klient s množstvom užitočných funkcií a intuitívne grafické užívateľské rozhranie.", + "FIleZilla Client is a fast and reliable cross-platform FTP, FTPS and SFTP client with lots of useful features and an intuitive graphical user interface.": "FileZilla Client je rýchly a spoľahlivý multiplatformový klient FTP, FTPS a SFTP s množstvom funkcií a intuitívnym grafickým rozhraním.", "Fail2Ban - Intrusion Prevention": "Fail2Ban - ochrana prihlasovania", "Fail2Ban Management": "Správa Fail2Ban", "Fail2Ban has been removed": "Fail2Ban bol odstránený", @@ -2425,9 +2428,9 @@ "Failed. See log:": "Zlyhalo. Pozrite záznam:", "Falling back to each installer with --auto-reinstall...": "Prepínam na jednotlivé inštalátory s --auto-reinstall...", "Falling back to manual paste mode.": "Prepínam na ručné vloženie.", - "Fast Usenet downloader with a SABnzbd-compatible API": "Fast Usenet downloader s SABnzbd kompatibilným API", + "Fast Usenet downloader with a SABnzbd-compatible API": "Rýchly nástroj na sťahovanie z Usenetu s API kompatibilným so SABnzbd", "Fast, modern web interface for qBittorrent": "Rýchle, moderné webové rozhranie pre qBittorrent", - "Faster-whisper is a reimplementation of OpenAI's Whisper model using CTranslate2, which is a fast inference engine for Transformer models. This container provides a Wyoming protocol server for faster-whisper.": "Rýchlejšie-whisper je reimplementácia modelu Whisper OpenAI pomocou CTranslate2, ktorý je rýchlym vyvodzovacím motorom pre modely Transformer. Tento kontajner poskytuje Wyoming protokol server pre rýchlejšie-šepkať.", + "Faster-whisper is a reimplementation of OpenAI's Whisper model using CTranslate2, which is a fast inference engine for Transformer models. This container provides a Wyoming protocol server for faster-whisper.": "Faster-whisper je nová implementácia modelu Whisper od OpenAI pomocou CTranslate2, rýchleho výpočtového enginu pre modely Transformer. Tento kontajner poskytuje server protokolu Wyoming pre faster-whisper.", "Fastfetch Logo Selection": "Výber loga pre Fastfetch", "Fastfetch configuration updated": "Nastavenie Fastfetch bolo aktualizované", "Fastfetch download URL retrieved successfully.": "Adresa na stiahnutie Fastfetch bola úspešne získaná.", @@ -2439,13 +2442,13 @@ "Fastfetch now displays: System optimised by: ProxMenux": "Fastfetch teraz zobrazuje: Systém optimalizoval: ProxMenux", "Fastfetch removed from system": "Fastfetch bol odstránený zo systému", "Fastfetch will start automatically in the console": "Fastfetch sa bude v konzole spúšťať automaticky", - "Ferdium is a desktop app that helps you organize how you use your favourite apps by combining them into one application.": "Ferdium je desktopová aplikácia, ktorá vám pomôže zorganizovať, ako používate svoje obľúbené aplikácie pomocou combi.", + "Ferdium is a desktop app that helps you organize how you use your favourite apps by combining them into one application.": "Ferdium je desktopová aplikácia, ktorá spája obľúbené aplikácie do jedného rozhrania a pomáha usporiadať ich používanie.", "Fetching NVIDIA driver versions supported by your GPU...": "Načítavam verzie ovládača NVIDIA podporované vaším GPU...", "Figurine installation and configuration completed successfully.": "Inštalácia a nastavenie Figurine boli úspešne dokončené.", "Figurine is not installed.": "Figurine nie je nainštalovaný.", "Figurine removed from system": "Figurine bol odstránený zo systému", "File bind mounts do not support spaces:": "Pripojenie súborov nepodporuje medzery:", - "File processing made easy!": "Spracovanie súborov bolo jednoduché!", + "File processing made easy!": "Jednoduché spracovanie súborov!", "File:": "Súbor:", "FileBrowser Quantum": "FileBrowser Quantum", "FileBrowser Quantum (new installation)": "FileBrowser Quantum (nová inštalácia)", @@ -2464,8 +2467,8 @@ "Finance & Budgeting": "Financie a rozpočet", "Find your device using https://finds.synology.com": "Zariadenie nájdete cez https://finds.synology.com", "Fingerprint:": "Odtlačok:", - "Firefly, the easiest using of WireGuard VPN server, plus version of wg-easy.": "Firefly, najjednoduchšie používanie WireGuard VPN servera, plus verzia wg-ľahké.", - "Firefox Browser, also known as Mozilla Firefox or simply Firefox, is a free and open-source web browser developed by the Mozilla Foundation and its subsidiary, the Mozilla Corporation. Firefox uses the Gecko layout engine to render web pages, which implements current and anticipated web standards.": "Firefox Browser, tiež známy ako Mozilla Firefox alebo jednoducho Firefox, je zadarmo a open-source webový prehliadač vyvinutý Mozilla nadácie a jej dcérskej spoločnosti, Mozilla Corporation. Firefox používa Gecko dispozičný motor na vytvorenie webových stránok, ktoré implementujú aktuálne a predpokladané webové štandardy.", + "Firefly, the easiest using of WireGuard VPN server, plus version of wg-easy.": "Firefly je jednoducho ovládateľný server WireGuard VPN, rozšírená verzia wg-easy.", + "Firefox Browser, also known as Mozilla Firefox or simply Firefox, is a free and open-source web browser developed by the Mozilla Foundation and its subsidiary, the Mozilla Corporation. Firefox uses the Gecko layout engine to render web pages, which implements current and anticipated web standards.": "Mozilla Firefox je bezplatný webový prehliadač s otvoreným zdrojovým kódom, ktorý vyvíjajú Mozilla Foundation a jej dcérska spoločnosť Mozilla Corporation. Na vykresľovanie webových stránok používa engine Gecko s podporou súčasných aj pripravovaných webových štandardov.", "Firewall allows port": "Firewall povoľuje port", "Firewall settings": "Nastavenia firewallu", "Firmware :": "Firmware :", @@ -2487,12 +2490,12 @@ "Fix: on the host, run": "Oprava: na hostovi spustite", "FlexGet web interface": "Webové rozhranie FlexGet", "Flexget is a multipurpose automation tool for all of your media.": "Flexget je viacúčelový automatizačný nástroj pre všetky vaše médiá.", - "Flowise 3.0.1 and later create the administrator account from the web interface, the first time it is opened.": "Flowise 3.0.1 a neskôr vytvoriť správca účtu z webového rozhrania, prvýkrát je otvorený.", - "Flycast is a multi-platform Sega Dreamcast, Naomi, Naomi 2, and Atomiswave emulator derived from reicast.": "Flycast je multiplatforma Sega Dreamcast, Naomi, Naomi 2, a atomiswave emulátor odvodený z reicastu.", + "Flowise 3.0.1 and later create the administrator account from the web interface, the first time it is opened.": "Flowise od verzie 3.0.1 vytvára účet správcu pri prvom otvorení webového rozhrania.", + "Flycast is a multi-platform Sega Dreamcast, Naomi, Naomi 2, and Atomiswave emulator derived from reicast.": "Flycast je multiplatformový emulátor Sega Dreamcast, Naomi, Naomi 2 a Atomiswave, odvodený z projektu reicast.", "Folder Name": "Názov priečinka", "Folders in /mnt": "Priečinky v /mnt", "Folders to share, separated by ; (for example /downloads/incoming;/my_movies)": "Priečinky na zdieľanie oddelené ;(napríklad /downloads/incoming;/my_movies)", - "Folding@home is a distributed computing project for simulating protein dynamics, including the process of protein folding and the movements of proteins implicated in a variety of diseases. It brings together citizen scientists who volunteer to run simulations of protein dynamics on their personal computers. Insights from this data are helping scientists to better understand biology, and providing new opportunities for developing therapeutics.": "Folding@home je distribuovaný počítačový projekt na simuláciu dynamiky bielkovín, vrátane procesu skladania bielkovín a pohybu proteínov, ktoré sa podieľajú na rôznych chorobách. To spája citizen vedci, ktorí dobrovoľne spustiť simulácie proteínovej dynamiky na svojich osobných počítačoch. Pohľady z týchto údajov pomáhajú vedcom lepšie pochopiť biológiu a poskytujú nové príležitosti na rozvoj terapií.", + "Folding@home is a distributed computing project for simulating protein dynamics, including the process of protein folding and the movements of proteins implicated in a variety of diseases. It brings together citizen scientists who volunteer to run simulations of protein dynamics on their personal computers. Insights from this data are helping scientists to better understand biology, and providing new opportunities for developing therapeutics.": "Folding@home je projekt distribuovaných výpočtov na simuláciu dynamiky bielkovín vrátane ich skladania a pohybov súvisiacich s rôznymi ochoreniami. Dobrovoľníci spúšťajú simulácie na svojich počítačoch a pomáhajú tak vedcom lepšie pochopiť biológiu a hľadať nové liečebné postupy.", "Follow post-restore progress live from ProxMenux Monitor → Backups tab after the reboot.": "Po reštarte sledujte priebeh po obnove naživo v ProxMenux Monitor → karta Backups.", "For LVM - Create mount directory and mount:": "Pre LVM - vytvoriť priečinok a pripojiť súborový systém:", "For ZFS, storage ID must start with a letter and use only letters, numbers, dot, dash, underscore or colon.": "Pri ZFS musí ID úložiska začínať písmenom a môže obsahovať iba písmená, čísla, bodku, pomlčku, podčiarkovník alebo dvojbodku.", @@ -2534,9 +2537,9 @@ "Free space on the host, or choose another storage for this backup from Manage installed OCI applications.": "Uvoľnite miesto na hostiteľskom serveri alebo v časti Spravovať nainštalované OCI aplikácie vyberte pre túto zálohu iné úložisko.", "Free up disk space": "Uvoľniť miesto na disku", "Free:": "Voľné:", - "FreeCAD is a general-purpose parametric 3D computer-aided design (CAD) modeler and a building information modeling (BIM) software application with finite element method (FEM) support.": "FreeCAD je všeobecné parametrická 3D modelka s počítačovou podporou (CAD) a softvérová aplikácia so stavebným informačným modelovaním (BIM) s podporou konečnej elementovej metódy (FEM).", + "FreeCAD is a general-purpose parametric 3D computer-aided design (CAD) modeler and a building information modeling (BIM) software application with finite element method (FEM) support.": "FreeCAD je univerzálny parametrický nástroj na počítačom podporované 3D navrhovanie (CAD) a informačné modelovanie stavieb (BIM) s podporou metódy konečných prvkov (FEM).", "French": "Francúzština", - "Freshrss is a free, self-hostable aggregator for rss feeds.": "Freshrss je voľný, self-hostinný aggregátor pre RSS kanály.", + "Freshrss is a free, self-hostable aggregator for rss feeds.": "FreshRSS je bezplatná čítačka a agregátor RSS kanálov na vlastnom serveri.", "Frigate WebUI": "Frigate WebUI", "Frigate uses the AMD GPU for detection once /config/config.yaml defines a detector with type: onnx.": "Frigate používa GPU AMD na detekciu, keď /config/config.yaml definuje detektor s type: onnx.", "Frigate uses the Coral once /config/config.yaml defines a detector with type: edgetpu and device: pci.": "Frigate používa Coral, keď /config/config.yaml definuje detektor s type: edgetpu a device: pci.", @@ -2553,7 +2556,7 @@ "Function Level Reset (FLR) not available": "Function Level Reset (FLR) nie je dostupný", "GID already in use:": "GID sa už používa:", "GID in CT": "GID v CT", - "GIMP is a free and open-source raster graphics editor used for image manipulation (retouching) and image editing, free-form drawing, transcoding between different image file formats, and more specialized tasks. It is extensible by means of plugins, and scriptable.": "GIMP je bezplatný a open-source rastrový editor, ktorý sa používa na manipuláciu s obrázkami (retušovanie) a úpravu obrázkov, kreslenie voľných foriem, transkódovanie medzi rôznymi formátmi obrazových súborov a ďalšie špecializované úlohy. Je rozšíriteľný pomocou pluginu, a skriptovateľné.", + "GIMP is a free and open-source raster graphics editor used for image manipulation (retouching) and image editing, free-form drawing, transcoding between different image file formats, and more specialized tasks. It is extensible by means of plugins, and scriptable.": "GIMP je bezplatný rastrový grafický editor s otvoreným zdrojovým kódom. Slúži na retušovanie a úpravu obrázkov, voľné kreslenie, prevod medzi obrazovými formátmi a ďalšie špecializované úlohy. Podporuje doplnky aj skriptovanie.", "GPU": "GPU", "GPU -> VM Mode Detected": "Zistený režim GPU -> VM", "GPU Already Added": "GPU už je pridaná", @@ -2596,10 +2599,10 @@ "GPU passthrough to VMs requires IOMMU to be enabled in the kernel.": "GPU passthrough do VM vyžaduje, aby bolo IOMMU zapnuté v kerneli.", "GPU passthrough was not applied.": "Priame priradenie GPU nebolo použité.", "GPU passthrough was skipped (no compatible GPU detected).": "Priame priradenie GPU bolo preskočené (nenašla sa vhodná GPU).", - "GPU recognition uses 8 GB of RAM and a limit of 4 CPU equivalents. These resources were tested in the lab and are not a universal minimum. Compatibility depends on the GPU, the models and the kernel. NVIDIA uses the GPUs of the Toolkit inventory; Intel keeps the CPU topology.": "Rozpoznávanie GPU používa 8 GB RAM a limit 4 CPU equivalentných. Tieto zdroje boli testované v laboratóriu a nie sú univerzálne minimum. Kompatibilita závisí od GPU, modelov a jadra. NVIDIA používa GPU inventára Toolkit; Intel udržuje topológiu CPU.", + "GPU recognition uses 8 GB of RAM and a limit of 4 CPU equivalents. These resources were tested in the lab and are not a universal minimum. Compatibility depends on the GPU, the models and the kernel. NVIDIA uses the GPUs of the Toolkit inventory; Intel keeps the CPU topology.": "Rozpoznávanie pomocou GPU používa 8 GB RAM a limit zodpovedajúci 4 CPU. Tieto prostriedky boli overené v laboratóriu a nie sú univerzálnym minimom. Kompatibilita závisí od GPU, modelov a jadra. NVIDIA používa GPU zo zoznamu Toolkit, Intel zachováva topológiu CPU.", "GPU removed from VM": "GPU odstránená z VM", "GPU removed from VM config": "GPU bola odstránená z nastavenia VM", - "GPU render device": "GPU zariadenie", + "GPU render device": "Vykresľovacie zariadenie GPU", "GPU switch complete: LXC mode prepared.": "Prepnutie GPU je hotové: LXC režim je pripravený.", "GPU switch complete: VM mode prepared.": "Prepnutie GPU je hotové: VM režim je pripravený.", "GPU switch mode completed. No reboot required.": "Režim GPU bol prepnutý. Reštart nie je potrebný.", @@ -2613,7 +2616,7 @@ "GRUB configuration updated": "Nastavenie GRUB bolo aktualizované", "GRUB_CMDLINE_LINUX_DEFAULT not found in GRUB config": "GRUB_CMDLINE_LINUX_DEFAULT sa v nastavení GRUB nenašlo", "GUI mode (if available)": "Grafický režim (ak je dostupný)", - "GZDoom is a feature centric port for all Doom engine games, based on ZDoom, adding an OpenGL renderer and powerful scripting capabilities.": "GZDoom je funkcia centrický port pre všetky hry Doom motora, založené na ZDoom, pridanie OpenGL kresbu a mocné skriptovanie schopnosti.", + "GZDoom is a feature centric port for all Doom engine games, based on ZDoom, adding an OpenGL renderer and powerful scripting capabilities.": "GZDoom je verzia enginu pre hry Doom založená na ZDoome. Pridáva vykresľovanie cez OpenGL a rozsiahle možnosti skriptovania.", "GZIP - slower, widely compatible": "GZIP - pomalší, široko kompatibilný", "Gaming & Leisure": "Hry a voľný čas", "Gateway is not installed.": "Brána nie je nainštalovaná.", @@ -2625,32 +2628,32 @@ "Generate a new key and authorize it on the server automatically (recommended)": "Vygenerovať nový kľúč a automaticky ho autorizovať na serveri (odporúčané)", "Generate a new key, show me the line to paste manually": "Vygenerovať nový kľúč a zobraziť riadok na ručné vloženie", "Generate a new keyfile": "Vygenerovať nový keyfile", - "Generated Paperless administrator": "Generovaný administrátor bez dokumentov", - "Generated Tandoor administrator": "Name", + "Generated Paperless administrator": "Vygenerovaný účet správcu Paperless", + "Generated Tandoor administrator": "Vygenerovaný účet správcu Tandoor", "Generated administrator login": "Prihlásenie generovaného správcu", "Generating OVF descriptor...": "Vytváram OVF popisovač...", "Generating dkms.conf...": "Vytváram dkms.conf...", "Generating manifest...": "Vytváram manifest...", "Generating missing locale:": "Vytváram chýbajúce locale:", - "Generic SCSI device associated with the drive (e.g. /dev/sg2)": "Generické zariadenie SCSI spojené s pohonom (napr. /dev/sg2)", + "Generic SCSI device associated with the drive (e.g. /dev/sg2)": "Všeobecné zariadenie SCSI priradené k mechanike (napr. /dev/sg2)", "German": "Nemčina", "Get a list of all your containers:": "Zobraziť zoznam všetkých kontajnerov:", "Get the actual disk path:": "Zistiť skutočnú cestu k disku:", "Get the container's storage information:": "Zistiť informácie o úložisku kontajnera:", - "Get up and running with large language models locally": "Vstaňte a bežte s veľkými jazykovými modelmi lokálne", + "Get up and running with large language models locally": "Jednoduché lokálne spúšťanie veľkých jazykových modelov", "Git installed": "Git nainštalovaný", - "GitQlient is a multi-platform Git client originally forked from QGit. Nowadays it goes beyond of just a fork and adds a lot of new functionality.": "GitQlient je multiplatforma Git klient pôvodne fork od QGit. V súčasnej dobe ide nad rámec len fork a pridáva veľa nových funkcií.", - "Github Desktop is an open source Electron-based GitHub app. It is written in TypeScript and uses React.": "Github Desktop je open source Electron-based app GitHub. Je napísaný v TypeScript a používa React.", + "GitQlient is a multi-platform Git client originally forked from QGit. Nowadays it goes beyond of just a fork and adds a lot of new functionality.": "GitQlient je multiplatformový klient Git, ktorý vznikol ako odnož QGit. Dnes prináša množstvo vlastných nových funkcií.", + "Github Desktop is an open source Electron-based GitHub app. It is written in TypeScript and uses React.": "GitHub Desktop je aplikácia GitHub s otvoreným zdrojovým kódom založená na Electrone. Je napísaná v TypeScripte a používa React.", "Global settings and SSH jail configured": "Globálne nastavenia a SSH jail sú nastavené", - "Gluetun/VPN not yet available: this suite does not route downloads through a VPN.": "Gluetun/VPN zatiaľ nie je k dispozícii: tento suite nie je trasa sťahovania cez VPN.", + "Gluetun/VPN not yet available: this suite does not route downloads through a VPN.": "Gluetun/VPN zatiaľ nie je dostupné: táto zostava nesmeruje sťahovanie cez VPN.", "Go to \"Manage custom paths\" and remove your custom entry that includes the destination": "Prejdite do \"Spravovať vlastné cesty\" a odstráňte vlastnú položku, ktorá obsahuje cieľ", "Google only ships an official libedgetpu APT repository for Debian/Ubuntu. Hardware passthrough is already written to": "Google poskytuje oficiálny libedgetpu APT repozitár iba pre Debian/Ubuntu. Hardvérový passthrough už je zapísaný do", "Graceful shutdown timed out.": "Korektné vypnutie nestihlo dobehnúť.", - "Grafana is a complete observability stack that allows you to monitor and analyze metrics, logs and traces. It allows you to query, visualize, alert on and understand your data no matter where it is stored.": "Grafana je kompletný sledovateľnosť zásobníka, ktorý umožňuje sledovať a analyzovať metriky, protokoly a stopy. Umožňuje vyhľadávať, vizualizovať, upozorniť a porozumieť vašim údajom bez ohľadu na to, kde sú uložené.", + "Grafana is a complete observability stack that allows you to monitor and analyze metrics, logs and traces. It allows you to query, visualize, alert on and understand your data no matter where it is stored.": "Grafana je komplexná platforma na monitorovanie a analýzu metrík, denníkov a trasovaní. Umožňuje údaje vyhľadávať, vizualizovať, nastavovať upozornenia a porozumieť im bez ohľadu na miesto uloženia.", "Grafana web interface": "Webové rozhranie Grafana", "Grav is a Fast, Simple, and Flexible, file-based Web-platform.": "Grav je rýchla, jednoduchá a flexibilná webová platforma na báze súborov.", - "Grocy (new installation; restored data keeps its credentials)": "Grocy (nová inštalácia; obnovené údaje si zachovajú svoje poverenia)", - "Grocy is an ERP system for your kitchen! Cut down on food waste, and manage your chores with this brilliant utility.": "Grocy je ERP systém pre vašu kuchyňu! Znížte plytvanie potravinami a spravte si prácu s týmto skvelým prínosom.", + "Grocy (new installation; restored data keeps its credentials)": "Grocy (nová inštalácia; pri obnove údajov zostávajú pôvodné prihlasovacie údaje)", + "Grocy is an ERP system for your kitchen! Cut down on food waste, and manage your chores with this brilliant utility.": "Grocy je systém ERP pre vašu kuchyňu. Pomáha obmedziť plytvanie potravinami a spravovať domáce povinnosti.", "Group": "Skupina", "Group 'sharedfiles' already exists inside the CT": "Skupina 'sharedfiles' už existuje vo vnútri CT", "Group GID:": "GID skupiny:", @@ -2688,13 +2691,13 @@ "Guided Repair Available": "Dostupná oprava so sprievodcom", "HA groups will be migrated to HA rules automatically": "HA skupiny sa automaticky prevedú na HA pravidlá", "HA services disabled (configs preserved)": "HA služby boli vypnuté (nastavenia zostali zachované)", - "HAOS One is a community image that runs Docker inside the container. The LXC stays unprivileged, but the inner AppArmor profiles may not be available. The first start downloads Home Assistant Core and its add-ons. If the check fails, the CT and /mnt/data are kept for diagnosis.": "HAOS One je obraz spoločenstva, ktorý prevádzkuje Docker vnútri kontajnera. LXC zostáva neprivilegovaný, ale vnútorné profily AppArmor nemusí byť k dispozícii. Prvý štart stiahne Home Assistant jadro a jeho doplnky. Ak kontrola zlyhá, CT a / mnt/ dáta sa uchovávajú na určenie diagnózy.", - "HAOS One profile declined": "Profil HAOS One klesol", - "HAOS One requires an unprivileged unmanaged LXC with nesting and keyctl, 2 cores, 2048 MB RAM, rootfs of at least 12 GB and /mnt/data of at least 16 GB on a container volume included in backups": "HAOS One requires an unprivileged unmanaged LXC with nestering and keyctl, 2 cores, 2048 MB RAM, rootfs najmenej 12 GB a /mnt/data najmenej 16 GB on a container volume included in backions", - "HTML5 Network Speed Test Server.": "HTML5 Network Speed Test Server.", + "HAOS One is a community image that runs Docker inside the container. The LXC stays unprivileged, but the inner AppArmor profiles may not be available. The first start downloads Home Assistant Core and its add-ons. If the check fails, the CT and /mnt/data are kept for diagnosis.": "HAOS One je komunitný obraz, ktorý spúšťa Docker v kontajneri. LXC zostáva neprivilegovaný, vnútorné profily AppArmor však nemusia byť dostupné. Pri prvom spustení sa stiahne Home Assistant Core a jeho doplnky. Ak kontrola zlyhá, kontajner a /mnt/data zostanú zachované na diagnostiku.", + "HAOS One profile declined": "Profil HAOS One bol odmietnutý", + "HAOS One requires an unprivileged unmanaged LXC with nesting and keyctl, 2 cores, 2048 MB RAM, rootfs of at least 12 GB and /mnt/data of at least 16 GB on a container volume included in backups": "HAOS One vyžaduje neprivilegovaný kontajner LXC typu unmanaged s nesting a keyctl, 2 jadrá, 2048 MB RAM, koreňový súborový systém s najmenej 12 GB a /mnt/data s najmenej 16 GB na dátovom zväzku kontajnera zahrnutom do záloh", + "HTML5 Network Speed Test Server.": "Server na testovanie rýchlosti siete cez HTML5.", "HTTP service check without a saved URL": "Kontrola služby HTTP bez uloženého URL", - "Habridge emulates Philips Hue API to other home automation gateways such as an Amazon Echo/Dot Gen 1 (gen 2 has issues discovering ha-bridge) or other systems that support Philips Hue. The Bridge handles basic commands such as On, Off and brightness commands of the hue protocol. This bridge can control most devices that have a distinct API.": "Habridge napodobňuje Philipsa Spoločnosť Amazon tvrdí, že spoločnosť LuxOpCo by mala mať prístup k webovým stránkam spoločnosti LuxOpCo. Most riadi základné príkazy, ako je On, Off a jas príkazy protokolu odtieňov. Tento most môže ovládať väčšinu zariadení, ktoré majú odlišné API.", - "HandBrake is an open-source tool, built by volunteers, for converting video from nearly any format to a selection of modern, widely supported codecs.": "HandBrake je open-source nástroj, postavený dobrovoľníkmi, pre konverziu video z takmer akéhokoľvek formátu na výber moderných, široko podporovaných kodekov.", + "Habridge emulates Philips Hue API to other home automation gateways such as an Amazon Echo/Dot Gen 1 (gen 2 has issues discovering ha-bridge) or other systems that support Philips Hue. The Bridge handles basic commands such as On, Off and brightness commands of the hue protocol. This bridge can control most devices that have a distinct API.": "ha-bridge emuluje API Philips Hue pre brány domácej automatizácie, napríklad Amazon Echo/Dot prvej generácie (druhá má problémy s vyhľadaním ha-bridge), a ďalšie kompatibilné systémy. Spracúva základné príkazy protokolu Hue na zapnutie, vypnutie a jas. Dokáže ovládať väčšinu zariadení s vlastným API.", + "HandBrake is an open-source tool, built by volunteers, for converting video from nearly any format to a selection of modern, widely supported codecs.": "HandBrake je nástroj s otvoreným zdrojovým kódom vytvorený dobrovoľníkmi na prevod videa z takmer ľubovoľného formátu do moderných, široko podporovaných kodekov.", "Hardening SSH: setting MaxAuthTries to 3...": "Spevňujem SSH: nastavujem MaxAuthTries na 3...", "Hardware acceleration for Emby": "Zrýchlenie hardvéru pre Emby", "Hardware acceleration for FileFlows": "Zrýchlenie hardvéru pre FileFlows", @@ -2712,11 +2715,11 @@ "Have a Private Social Space Hosted on Your Site": "Majte na svojej stránke vlastný sociálny priestor", "Have valid backups of all VMs and containers": "Majte platné zálohy všetkých VM a kontajnerov", "Health check failed:": "Kontrola stavu zlyhala:", - "Healthchecks is a watchdog for your cron jobs. It's a web server that listens for pings from your cron jobs, plus a web interface.": "Healthchecks je strážny pes pre vaše Cron pracovných miest. Je to webový server, ktorý počúva na ping z vašich Cron jobs, plus webové rozhranie.", + "Healthchecks is a watchdog for your cron jobs. It's a web server that listens for pings from your cron jobs, plus a web interface.": "Healthchecks dohliada na úlohy cron. Webový server prijíma ich kontrolné hlásenia a zobrazuje ich vo webovom rozhraní.", "HedgeDoc gives you access to all your files wherever you are.": "HedgeDoc vám umožní prístup ku všetkým súborom kdekoľvek ste.", "HedgeDoc is a self-hosted collaborative Markdown editor for notes.": "HedgeDoc je vlastný spoločný editor Markdown pre poznámky.", "Heimdall is a way to organise all those links to your most used web sites and web applications in a simple way.": "Heimdall je spôsob, ako zorganizovať všetky tieto odkazy na vaše najpoužívanejšie webové stránky a webové aplikácie jednoduchým spôsobom.", - "Helium is a Chromium-based web browser made for people, with love. Privacy-first with unbiased ad-blocking.": "Helium je Chromium-založený webový prehliadač vyrobený pre ľudí, s láskou. Privacy-first with unsised ad-blocking.", + "Helium is a Chromium-based web browser made for people, with love. Privacy-first with unbiased ad-blocking.": "Helium je prehliadač založený na Chromiu, vytvorený s dôrazom na používateľa. Uprednostňuje súkromie a nestranné blokovanie reklám.", "Help & Info (commands)": "Pomoc a informácie (príkazy)", "Help & Information": "Pomoc a informácie", "Help and Info": "Pomoc a informácie", @@ -2733,7 +2736,7 @@ "High risk confirmation": "Potvrdenie vysokého rizika", "High-Risk GPU Power State": "Rizikový stav napájania GPU", "Home Assistant": "Home Assistant", - "Home Assistant Core - Open source home automation that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts. Perfect to run on a Raspberry Pi or a local server": "Home Assistant Core - Open source domácej automatizácie, ktorá kladie miestne ovládanie a súkromie ako prvý. Poháňané celosvetovou komunitou drotárov a domácich nadšencov. Ideálne pre beh na Malinová Pi alebo miestny server", + "Home Assistant Core - Open source home automation that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts. Perfect to run on a Raspberry Pi or a local server": "Home Assistant Core je systém domácej automatizácie s otvoreným zdrojovým kódom, ktorý uprednostňuje lokálne ovládanie a súkromie. Rozvíja ho celosvetová komunita nadšencov a domácich majstrov. Hodí sa na Raspberry Pi alebo lokálny server.", "Home Assistant OS cannot be checked without an IP address": "Home Assistant OS nemožno skontrolovať bez IP adresy", "Home Assistant OS did not pass the Supervisor, Core and Observer checks": "Home Assistant OS neprešiel dozorcom, základnými a pozorovateľmi", "Home Assistant OS ready: Supervisor, Core and Observer running": "Home Assistant OS pripravený: Supervízor, jadro a pozorovateľ beží", @@ -2767,7 +2770,7 @@ "Host Path": "Cesta na hostovi", "Host Path:": "Cesta na hostovi:", "Host Storage (NFS / Samba via Proxmox)": "Úložisko na serveri (NFS / Samba cez Proxmox)", - "Host USB bus": "Host USB autobus", + "Host USB bus": "USB zbernica hostiteľa", "Host USB node (e.g. /dev/ttyACM0 or /dev/bus/usb/003/004)": "Hostiteľský uzol USB (napr. /dev/ttyACM0 alebo /dev/bus/usb/003/004)", "Host VFIO config was already up to date — no reboot needed.": "VFIO nastavenie hosta už bolo aktuálne - reštart nie je potrebný.", "Host VFIO configuration already up to date": "VFIO nastavenie hosta je už aktuálne", @@ -2813,8 +2816,8 @@ "Host kernel module loaded:": "Načítaný modul jadra hostiteľa:", "Host monitor configured: shared PID and network namespaces, LXCFS disabled in this container": "Konfigurovaný monitor hostiteľa: zdieľané PID a sieťové priestory, LXCFS vypnuté v tomto kontajneri", "Host monitor verified: PID and network namespaces and memory match the host": "Monitor hostiteľa overený: PID a sieťové názvy a pamäť zodpovedajú hostiteľovi", - "Host monitor: shared PID/network and privileged access. A compromised image could affect Proxmox. Use only on trusted networks.": "Monitor hostiteľa: spoločná PID/sieť a privilegovaný prístup. Kompromisný obraz by mohol ovplyvniť Proxmox. Používajte len na dôveryhodných sieťach.", - "Host monitoring declined": "Monitorovanie hostiteľa sa znížilo", + "Host monitor: shared PID/network and privileged access. A compromised image could affect Proxmox. Use only on trusted networks.": "Monitorovanie hostiteľa so zdieľaným priestorom PID, sieťou a privilegovaným prístupom. Napadnutý obraz by mohol ohroziť Proxmox. Používajte iba v dôveryhodných sieťach.", + "Host monitoring declined": "Monitorovanie hostiteľa bolo odmietnuté", "Host path for": "Cesta na hostiteľovi pre", "Host paths found in container configs or saved records (not targeted for removal):": "Cesty hostiteľa nájdené v konfiguráciách kontajnerov alebo v uložených záznamoch (neodstraňujú sa):", "Host permissions applied (o+rwx + default ACL) — unprivileged LXCs can read/write through bind-mounts": "Oprávnenia hosta boli nastavené (o+rwx + predvolené ACL) - neprivilegované LXC môžu čítať/zapisovať cez bind mounty", @@ -2841,7 +2844,7 @@ "ID already in use. Please choose another.": "Toto ID sa už používa. Vyberte iné.", "IGDB": "IGDB", "IGDB Client ID": "ID klienta IGDB", - "IGDB Client Secret": "IGDB klient tajomstvo", + "IGDB Client Secret": "Tajný kľúč klienta IGDB", "IMPORTANT": "DÔLEŽITÉ", "IMPORTANT NOTES:": "DÔLEŽITÉ POZNÁMKY:", "IMPORTANT PREREQUISITES:": "DÔLEŽITÉ PREDPOKLADY:", @@ -2880,7 +2883,7 @@ "IP": "IP", "IP address": "IP adresa", "IP address and firewall of the host": "IP adresa a firewall hostiteľa", - "IP address of this container for the certificate (0.0.0.0 if unknown)": "IP adresa tohto kontajnera pre osvedčenie (0,0.0, ak nie je známe)", + "IP address of this container for the certificate (0.0.0.0 if unknown)": "IP adresa tohto kontajnera pre certifikát (0.0.0.0, ak nie je známa)", "IP address:": "IP adresa:", "IP or hostname of the PVE node hosting the Borg server LXC:": "IP alebo hostname PVE uzla, na ktorom beží Borg server LXC:", "IPv4 address of the container": "IPv4 adresa kontajnera", @@ -2927,7 +2930,7 @@ "If you are sure you want to use it, remove the RAID metadata or format it manually using external tools.": "Ak ste sure, chcete ho použiť, odstráňte metadáta RAID alebo ich naformátujte manuálne pomocou externých nástrojov.", "If you choose No, install": "Ak vyberiete Nie, nainštalujte", "If you continue, some adjustments may be duplicated or conflict with those already made by xshok.": "Ak budete pokračovať, niektoré úpravy sa môžu zdvojiť alebo byť v konflikte s tým, čo už urobil xshok.", - "If you do not know what a variable does, answer No: the image keeps its own value.": "Ak neviete, čo premenná robí, odpovedzte Nie: obrázok si zachováva svoju vlastnú hodnotu.", + "If you do not know what a variable does, answer No: the image keeps its own value.": "Ak neviete, čo premenná robí, odpovedzte Nie. Obraz si zachová vlastnú hodnotu.", "If you have a Coral M.2 / PCIe device that is physically installed but not detected by lspci, cancel here and check your hardware first before proceeding.": "Ak máte zariadenie Coral M.2 / PCIe, ktoré je fyzicky nainštalované, ale nie je detekované lspci, zrušte to tu a pred pokračovaním najskôr skontrolujte hardvér.", "If you lose connectivity, you can restore from backup using the console.": "Ak stratíte pripojenie, môžete obnoviť zálohu cez konzolu.", "If you lose or reinstall this host without a copy of the passphrase somewhere else (password manager, offline note, another host, USB stick...), every encrypted archive in this Borg repository becomes UNRECOVERABLE.": "Ak tento host stratíte alebo preinštalujete bez kópie frázy niekde inde (správca hesiel, offline poznámka, iný host, USB kľúč...), každý šifrovaný archív v tomto Borg repozitári bude NEOBNOVITEĽNÝ.", @@ -3038,7 +3041,7 @@ "Increasing maximum file system open files...": "Zvyšujem maximálny počet otvorených súborov v systéme...", "Increasing various system limits...": "Zvyšujem rôzne systémové limity...", "Independent LXC applications installed": "Inštalované nezávislé aplikácie LXC", - "Independent LXCs: no main container or hookscript. Each one keeps its own Start with Proxmox setting.": "Nezávislé LXCs: žiadny hlavný kontajner alebo hákový skript. Každý si ponechá svoj vlastný štart s nastavením Proxmox.", + "Independent LXCs: no main container or hookscript. Each one keeps its own Start with Proxmox setting.": "Samostatné kontajnery LXC bez hlavného kontajnera a skriptu hookscript. Každý si zachová vlastné nastavenie spúšťania spolu s Proxmoxom.", "Independent applications, without a main container.": "Nezávislé aplikácie bez hlavného kontajnera.", "Indexers and quality profiles still need to be configured.": "Indexery a kvalitné profily ešte treba nakonfigurovať.", "Inherited retention:": "Prevzaté uchovávanie:", @@ -3048,7 +3051,7 @@ "Initial Paperless-ngx administrator": "Počiatočný správca Paperless-ngx", "Initial Tandoor administrator": "Počiatočný správca Tandoor", "Initial administrator created:": "Pôvodný správca vytvorený:", - "Initial administrator user": "Počiatočný užívateľ správcu", + "Initial administrator user": "Počiatočné používateľské meno správcu", "Initializing Borg repository if needed...": "Pripravujem Borg repozitár, ak je to potrebné...", "Initiator IQN is authorised on the target": "IQN iniciátora je na targete povolené", "Initiator IQN:": "IQN iniciátora:", @@ -3206,12 +3209,12 @@ "Intel render device for recognition": "Zariadenie na rozpoznávanie informácií", "Intel/AMD (VA-API and QSV)": "Intel/AMD (VA-API a QSV)", "Intel/AMD (VA-API)": "Intel/AMD (VA-API)", - "Intel/AMD (streaming rendering and encoding)": "Intel/AMD (prevádzanie a kódovanie)", + "Intel/AMD (streaming rendering and encoding)": "Intel/AMD (vykresľovanie a kódovanie streamu)", "Intel/AMD DRM node (device only)": "Intel/AMD uzol DRM (iba zariadenie)", "Intel/AMD VA-API": "Intel/AMD VA-API", "Intel/AMD VA-API (no OpenCL mod)": "Intel/AMD VA-API (bez OpenCL mod)", - "Intel/AMD render node": "Uzol Intel/AMD", - "IntelliJ IDEA helps you write code faster with tools that eliminate tedious tasks and let you focus on what matters – building great software.": "IntelliJ IDEA vám pomôže napísať kód rýchlejšie s nástrojmi, ktoré eliminujú únavné úlohy a umožní vám sústrediť sa na to, čo je dôležité", + "Intel/AMD render node": "Vykresľovací uzol Intel/AMD", + "IntelliJ IDEA helps you write code faster with tools that eliminate tedious tasks and let you focus on what matters – building great software.": "IntelliJ IDEA pomáha písať kód rýchlejšie. Automatizuje zdĺhavé úlohy a umožňuje sústrediť sa na tvorbu kvalitného softvéru.", "Interactive (guided, prompts visible)": "Interaktívne (sprievodca, otázky viditeľné)", "Interactive process viewer (press q to exit)": "Interaktívny prehliadač procesov (ukončíte klávesom q)", "Interface": "Rozhranie", @@ -3225,7 +3228,7 @@ "Interfaces to Remove": "Rozhrania na odstránenie", "Internal error: NVIDIA installer path is empty or file not found.": "Vnútorná chyba: cesta k NVIDIA inštalátoru je prázdna alebo sa súbor nenašiel.", "Internal error: missing arguments in pmx_prepare_host_shared_dir": "Vnútorná chyba: chýbajú argumenty v pmx_prepare_host_shared_dir", - "Internal subnet of the tunnel (change it only if it clashes)": "Interný podsieť tunela (zmeniť ho, len ak sa zrazí)", + "Internal subnet of the tunnel (change it only if it clashes)": "Interná podsieť tunela (zmeňte ju iba pri konflikte)", "Interrupted operation": "Prerušená operácia", "Interrupted operation:": "Prerušená operácia:", "Interrupted stack operation found": "Našla sa prerušená operácia celej aplikácie", @@ -3328,7 +3331,7 @@ "Invalid process limits format": "Neplatný formát obmedzení procesu", "Invalid read-only option": "Neplatná možnosť iba na čítanie", "Invalid registry digest": "Neplatné trávenie registra", - "Invalid remote name": "Neplatné vzdialené meno", + "Invalid remote name": "Neplatný názov vzdialeného úložiska", "Invalid remote path": "Neplatná vzdialená cesta", "Invalid repair version:": "Neplatná verzia opravy:", "Invalid resource value": "Neplatná hodnota prostriedkov", @@ -3353,7 +3356,7 @@ "Invalid storage": "Neplatné úložisko", "Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "Neplatné ID úložiska. Použite iba písmená, čísla, pomlčky a podčiarkovníky.", "Invalid storage name": "Neplatný názov úložiska", - "Invalid suite application": "Neplatná aplikácia", + "Invalid suite application": "Neplatná aplikácia zostavy", "Invalid sysctl value:": "Neplatná sysctl hodnota:", "Invalid template storage": "Neplatná pamäť šablóny", "Invalid template storage name.": "Neplatný názov úložiska šablóny.", @@ -3363,9 +3366,9 @@ "Invalid username or password.": "Nesprávne meno používateľa alebo heslo.", "Invalid variable name": "Neplatný názov premennej", "Invalid variable name:": "Neplatný názov premennej:", - "Invalid volume size": "Neplatná veľkosť objemu", + "Invalid volume size": "Neplatná veľkosť dátového zväzku", "Invalid volume size:": "Neplatná veľkosť trvalého úložiska:", - "Invalid volume target": "Neplatný cieľ objemu", + "Invalid volume target": "Neplatná cieľová cesta dátového zväzku", "IoT & Smart Home": "IoT a inteligentná domácnosť", "Is the value a password or secret?": "Je hodnota heslo alebo tajomstvo?", "Issue": "Problém", @@ -3392,17 +3395,17 @@ "Its final cleanup did not complete. Select the stack again in the OCI management menu to complete it.": "Záverečné upratovanie sa nedokončilo. Ak ho chcete dokončiť, v ponuke správy OCI znova vyberte danú aplikáciu.", "Its labels are not applied: they are read by other Docker tools.": "Nepoužijú sa jej označenia: čítajú ich iné nástroje Docker.", "JC Channel logo applied": "Logo JC Channel bolo použité", - "JDownloader 2 with browser GUI and MyJDownloader support": "JDownloader 2 s podporou prehliadača a MyJDownloader", + "JDownloader 2 with browser GUI and MyJDownloader support": "JDownloader 2 s grafickým rozhraním v prehliadači a podporou MyJDownloader", "JDownloader WebUI": "JDownloader WebUI", "JSON output for scripts": "JSON výstup pre skripty", - "Jackett works as a proxy server: it translates queries from apps (Sonarr, SickRage, CouchPotato, Mylar, etc) into tracker-site-specific http queries, parses the html response, then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches. Jackett is a single repository of maintained indexer scraping & translation logic - removing the burden from other apps.": "Jackett funguje ako proxy server: prekladá otázky z aplikácií (Sonarr, SickRage, CouchPotato, Mylar, atď) do tracker-site-specific http dotazov, analyzuje html odpoveď, potom pošle výsledky späť do žiadajúceho softvéru. To umožňuje získať najnovšie nahrávanie (ako RSS) a vykonávanie vyhľadávania. Jackett je jediný úložisko udržiavaného indexer škrabanie a preklad logiky - odstránenie záťaže z iných aplikácií.", + "Jackett works as a proxy server: it translates queries from apps (Sonarr, SickRage, CouchPotato, Mylar, etc) into tracker-site-specific http queries, parses the html response, then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches. Jackett is a single repository of maintained indexer scraping & translation logic - removing the burden from other apps.": "Jackett funguje ako proxy server: dotazy z aplikácií Sonarr, SickRage, CouchPotato, Mylar a ďalších prevádza na HTTP dotazy pre jednotlivé trackery, spracuje ich HTML odpovede a výsledky vráti aplikácii. Umožňuje vyhľadávať aj získavať najnovšie príspevky cez RSS. Zdieľaná logika získavania a prevodu údajov z indexerov odbremeňuje ostatné aplikácie.", "Jellyfin WebUI": "Jellyfin WebUI", "Jellyfin configuration applied:": "Použitá konfigurácia Jellyfin:", "Jellyfin did not create encoding.xml before the timeout": "Jellyfin nevytvoril kódovanie.xml pred prestávkou", "Jellyfin has not created encoding.xml in any declared path": "Jellyfin nevytvoril kódovanie.xml v žiadnej deklarovanej ceste", "Jellyseerr is a free and open source software application for managing requests for your media library.": "Jellyseerr je bezplatná a open source softvérová aplikácia pre správu žiadostí o mediálnu knižnicu.", - "Jenkins Continuous Integration and Delivery server.": "Jenkins Nepretržitá integrácia a doručovací server.", - "Jenkins unlock": "Jenkins odomknúť", + "Jenkins Continuous Integration and Delivery server.": "Jenkins – server na priebežnú integráciu a dodávanie softvéru.", + "Jenkins unlock": "Odomknutie Jenkins", "Job": "Job", "Job ID (letters, numbers, - _)": "ID úlohy (písmená, čísla, - _)", "Job ID (optional)": "ID úlohy (voliteľné)", @@ -3415,7 +3418,7 @@ "Job enabled:": "Úloha zapnutá:", "Job selection returned empty id — aborting.": "Výber úlohy vrátil prázdne ID - prerušujem.", "Job updated successfully.": "Úloha bola úspešne aktualizovaná.", - "Joplin is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks.": "Joplin je bezplatná, open source note, pričom a to-do aplikácie, ktoré dokážu zvládnuť veľký počet poznámok usporiadaných do notebookov.", + "Joplin is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks.": "Joplin je bezplatná aplikácia s otvoreným zdrojovým kódom na poznámky a úlohy. Zvláda veľké množstvo poznámok usporiadaných do zápisníkov.", "Journald configuration adjusted to": "Nastavenie journald bolo upravené na", "Journald configuration is already optimized": "Nastavenie journald už je optimalizované", "Journald configuration updated and service restarted": "Nastavenie journald bolo aktualizované a služba reštartovaná", @@ -3426,12 +3429,12 @@ "KVM MSR options added to /etc/modprobe.d/kvm.conf": "Možnosti KVM MSR boli pridané do /etc/modprobe.d/kvm.conf", "KVM MSR options ensured in /etc/modprobe.d/kvm.conf": "Možnosti KVM MSR sú zaistené v /etc/modprobe.d/kvm.conf", "KVM MSR options not present, nothing to revert": "Možnosti KVM MSR nie sú prítomné, nie je čo vracať späť", - "Kali-linux - is an Advanced Penetration Testing Linux distribution used for Penetration Testing, Ethical Hacking and network security assessments. KALI LINUX ™ is a trademark of OffSec.": "Kali-linux - je Advanced Penetration Testing Linux distribúcia používaná pre Penetration Testing, Ethical Hacking a posúdenie bezpečnosti siete. KALI LINUX TM je ochranná známka OffSec.", - "Kasm Workspaces is a docker container streaming platform for delivering browser-based access to desktops, applications, and web services. Kasm uses devops-enabled Containerized Desktop Infrastructure (CDI) to create on-demand, disposable, docker containers that are accessible via web browser. Example use-cases include Remote Browser Isolation (RBI), Data Loss Prevention (DLP), Desktop as a Service (DaaS), Secure Remote Access Services (RAS), and Open Source Intelligence (OSINT) collections.": "Kasm Workspaces je docker kontajner streaming platforma pre poskytovanie prístupu prehliadača na desktopy, aplikácie a webové služby. Kasm používa devops-povolený Containerized Desktop Infrastructure (CDI) vytvoriť na požiadanie, jednorazové, docker kontajnery, ktoré sú prístupné prostredníctvom webového prehliadača. Príkladom prípadov použitia je izolácia diaľkového prehliadača (RBI), prevencia straty dát (DLP), zbierka Desktop ako služba (DaaS), Secure Remote Access Services (RAS) a Open Source Intelligence (OSINT).", - "Kavita is a fast, feature rich, cross platform reading server. Built with a focus for being a full solution for all your reading needs. Setup your own server and share your reading collection with your friends and family!": "Kavita je rýchly, funkcie bohatý, cross platform čítanie servera. Postavený so zameraním na to, že úplné riešenie pre všetky vaše potreby čítania. Nastavte si vlastný server a podeľte sa o kolekciu čítania so svojimi priateľmi a rodinou!", - "Kavita is a free and open source web based Comic and Book Server.": "Kavita je zadarmo a open source web based Comic and Book Server.", - "Kdenlive is a powerful free and open source cross-platform video editing program made by the KDE community. Feature rich and production ready.": "Kdenlive je výkonný voľný a otvorený zdroj multiplatformový program na úpravu videa vytvorený komunitou KDE. Motív bohatý a výroba pripravený.", - "KeePassXC is a free and open-source password manager. It started as a community fork of KeePassX (itself a cross-platform port of KeePass).": "KeePassXC je bezplatný a open-source správca hesiel. Začalo sa to ako komunita fork z KeePassX (samotne ako cross-platform port KeePass).", + "Kali-linux - is an Advanced Penetration Testing Linux distribution used for Penetration Testing, Ethical Hacking and network security assessments. KALI LINUX ™ is a trademark of OffSec.": "Kali Linux je linuxová distribúcia určená na penetračné testovanie, etický hacking a posudzovanie bezpečnosti sietí. KALI LINUX™ je ochranná známka spoločnosti OffSec.", + "Kasm Workspaces is a docker container streaming platform for delivering browser-based access to desktops, applications, and web services. Kasm uses devops-enabled Containerized Desktop Infrastructure (CDI) to create on-demand, disposable, docker containers that are accessible via web browser. Example use-cases include Remote Browser Isolation (RBI), Data Loss Prevention (DLP), Desktop as a Service (DaaS), Secure Remote Access Services (RAS), and Open Source Intelligence (OSINT) collections.": "Kasm Workspaces sprístupňuje pracovné plochy, aplikácie a webové služby v kontajneroch Docker cez prehliadač. Infraštruktúra CDI vytvára na požiadanie dočasné kontajnery. Využíva sa na izoláciu vzdialeného prehliadača (RBI), prevenciu úniku údajov (DLP), pracovnú plochu ako službu (DaaS), bezpečný vzdialený prístup (RAS) a získavanie informácií z otvorených zdrojov (OSINT).", + "Kavita is a fast, feature rich, cross platform reading server. Built with a focus for being a full solution for all your reading needs. Setup your own server and share your reading collection with your friends and family!": "Kavita je rýchly multiplatformový server na čítanie s množstvom funkcií. Ponúka úplné riešenie pre vašu knižnicu. Vytvorte si vlastný server a zdieľajte svoju zbierku s rodinou a priateľmi.", + "Kavita is a free and open source web based Comic and Book Server.": "Kavita je bezplatný webový server na komiksy a knihy s otvoreným zdrojovým kódom.", + "Kdenlive is a powerful free and open source cross-platform video editing program made by the KDE community. Feature rich and production ready.": "Kdenlive je výkonný bezplatný multiplatformový editor videa s otvoreným zdrojovým kódom od komunity KDE. Ponúka množstvo funkcií a je pripravený na profesionálnu prácu.", + "KeePassXC is a free and open-source password manager. It started as a community fork of KeePassX (itself a cross-platform port of KeePass).": "KeePassXC je bezplatný správca hesiel s otvoreným zdrojovým kódom. Vznikol ako komunitná odnož KeePassX, ktorý je multiplatformovou verziou KeePass.", "Keep GPU in LXC config (disable Start on boot)": "Ponechať GPU v nastavení LXC (vypnúť štart pri boote)", "Keep GPU in LXC config + disable Start on boot": "Ponechať GPU v nastavení LXC + vypnúť štart pri boote", "Keep GPU in VM config (disable Start on boot)": "Ponechať GPU v nastavení VM (vypnúť štart pri boote)", @@ -3464,17 +3467,17 @@ "Keyfile recovery — pick source host": "Obnova keyfile - vyberte zdrojový host", "Keyfile removed.": "Keyfile odstránený.", "Keyrings method failed; trying apt-key fallback": "Metóda keyrings zlyhala; skúšam náhradný apt-key postup", - "KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - Cross Platform a Open Source Electronics Design Automation Suite.", + "KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad – multiplatformový balík s otvoreným zdrojovým kódom na návrh elektroniky.", "Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai nemá predvolený účet. Do kontajnera vstúpite príkazom: pct enter {main_vmid}", - "Kimai is a professional grade time-tracking application, free and open-source.": "Kimai je profesionálna časová aplikácia, zadarmo a open-source.", - "Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa je výkonný nástroj navrhnutý tak, aby vám plnú kontrolu nad vaše mediálne knižnice. S Kometa, môžete vziať svoje prispôsobenie na ďalšiu úroveň, s granulovanou kontrolou nad metaúdaje, zbierky, prekrývanie, a oveľa viac.", - "Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa číta svoju konfiguráciu z /config/config.yml a kontajner iba lode /config/config.yml.templát. Skopírujte šablónu pre config.yml, vyplňte required Plex a TMDb pripojenia, potom reštartujte kontajner.", - "Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga je mediálny server pre vaše komiksy, mangasy, BD, časopisy a eBooks.", - "Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita je profesionálny zadarmo a open source maliarsky program. Je vyrobený umelcami, ktorí chcú vidieť dostupné umelecké nástroje pre každého.", + "Kimai is a professional grade time-tracking application, free and open-source.": "Kimai je profesionálna bezplatná aplikácia na sledovanie pracovného času s otvoreným zdrojovým kódom.", + "Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa poskytuje podrobnú kontrolu nad mediálnymi knižnicami. Umožňuje prispôsobiť metadáta, kolekcie, grafické prekrytia a ďalšie prvky.", + "Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa číta nastavenia z /config/config.yml, kontajner však obsahuje iba /config/config.yml.template. Skopírujte šablónu do config.yml, vyplňte požadované pripojenia Plex a TMDb a kontajner reštartujte.", + "Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga je mediálny server na komiksy, mangu, grafické romány, časopisy a elektronické knihy.", + "Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita je profesionálny bezplatný maliarsky program s otvoreným zdrojovým kódom. Vytvárajú ho umelci, ktorí chcú sprístupniť umelecké nástroje každému.", "LAN access to the kept containers (stack configuration incomplete):": "Prístup LAN k uloženým kontajnerom (konfigurácia koša neúplná):", "LAN address applied to the application URLs": "LAN adresa použitá na URL aplikácie", "LLM App Development Platform": "Platforma vývoja aplikácií LLM", - "LM Studio can run local AI models like gpt-oss, Llama, Gemma, Qwen, and DeepSeek privately on your computer.": "LM Studio môže spustiť miestne modely AI ako Gpt-oss, Llama, Gemma, Qwen, a DeepSeek súkromne na vašom počítači.", + "LM Studio can run local AI models like gpt-oss, Llama, Gemma, Qwen, and DeepSeek privately on your computer.": "LM Studio umožňuje spúšťať modely AI ako gpt-oss, Llama, Gemma, Qwen a DeepSeek lokálne a súkromne na vašom počítači.", "LUNs appear as block devices assignable to VMs": "LUNy sa zobrazia ako blokové zariadenia priraditeľné k VM", "LVM PV headers check completed": "Kontrola LVM PV hlavičiek dokončená", "LVM physical volume detected": "Zistený fyzický zväzok LVM", @@ -3501,18 +3504,18 @@ "LZO - fastest, weaker ratio": "LZO - najrýchlejší, slabší pomer", "Lab interruption after installing the new container": "Prerušenie laboratória po inštalácii nového kontajnera", "Lab interruption after protecting the data": "Prerušenie laboratória po ochrane údajov", - "Label Studio is an open source data labeling tool. It lets you label data types like audio, text, images, videos, and time series with a simple and straightforward UI and export to various model formats. It can be used to prepare raw data or improve existing training data to get more accurate ML models.": "Label Studio je open source nástroj na označovanie dát. Umožňuje označiť dátové typy ako audio, text, obrázky, videá a časové rady jednoduchým a jednoduchým UI a exportovať do rôznych formátov modelu. Môže sa použiť na prípravu nespracovaných údajov alebo zlepšenie existujúcich údajov o odbornej príprave s cieľom získať viac modelov accurate ML.", + "Label Studio is an open source data labeling tool. It lets you label data types like audio, text, images, videos, and time series with a simple and straightforward UI and export to various model formats. It can be used to prepare raw data or improve existing training data to get more accurate ML models.": "Label Studio je nástroj s otvoreným zdrojovým kódom na označovanie údajov, napríklad zvuku, textu, obrázkov, videa či časových radov. Ponúka jednoduché rozhranie a export do rôznych formátov pre modely. Pomáha pripraviť alebo zlepšiť tréningové údaje na presnejšie modely strojového učenia.", "Label:": "Štítok:", "Language Change": "Zmena jazyka", "Language changed to": "Jazyk bol zmenený na", - "Language/locale (e.g. es_ES.UTF-8; translation of every application is not guaranteed)": "Jazyk/lokál (napr. es ES.UTF-8; preklad každej aplikácie nie je zaručený)", + "Language/locale (e.g. es_ES.UTF-8; translation of every application is not guaranteed)": "Jazyk a miestne nastavenia (napr. es_ES.UTF-8; preklad každej aplikácie nie je zaručený)", "Last 50 kernel log lines": "Posledných 50 riadkov logu jadra", "Last run:": "Posledné spustenie:", "Last system boot time": "Čas posledného štartu systému", "Latest version:": "Najnovšia verzia:", "Launching GPU passthrough assistant for VM": "Spúšťam sprievodcu priamym priradením GPU pre VM", - "Lazylibrarian is a program to follow authors and grab metadata for all your digital reading needs. It uses a combination of Goodreads Librarything and optionally GoogleBooks as sources for author info and book info. This container is based on the DobyTang fork.": "Lazylibrarian je program sledovať autorov a chytiť metaúdaje pre všetky vaše potreby digitálneho čítania. Používa combination of Goodreads Librarything a voliteľne GoogleBooks ako zdroje pre autora info a book info. Tento kontajner je založený na DobyTang fork.", - "Ldap-auth software is for authenticating users who request protected resources from servers proxied by nginx. It includes a daemon (ldap-auth) that communicates with an authentication server, and a webserver daemon that generates an authentication cookie based on the user’s credentials. The daemons are written in Python for use with a Lightweight Directory Access Protocol (LDAP) authentication server (OpenLDAP or Microsoft Windows Active Directory 2003 and 2012).": "Software Llap-auth je určený pre autentifikáciu užívateľov, ktorí žiadajú chránené zdroje zo serverov proxied by nginx. Obsahuje démon (ldap-auth), ktorý komunikuje s autentifikačným serverom, a webserver démon, ktorý generuje autentifikačný cookie na základe užívateľa credentials. Démoni sú napísané v Python pre použitie s ľahkou Directory Access Protocol (LDAP) autentifikačný server (OpenLDAP alebo Microsoft Windows Active Directory 2003 a 2012).", + "Lazylibrarian is a program to follow authors and grab metadata for all your digital reading needs. It uses a combination of Goodreads Librarything and optionally GoogleBooks as sources for author info and book info. This container is based on the DobyTang fork.": "LazyLibrarian sleduje autorov a získava metadáta pre vašu digitálnu knižnicu. Informácie o autoroch a knihách čerpá z Goodreads, LibraryThing a voliteľne Google Books. Tento kontajner vychádza z vetvy DobyTang.", + "Ldap-auth software is for authenticating users who request protected resources from servers proxied by nginx. It includes a daemon (ldap-auth) that communicates with an authentication server, and a webserver daemon that generates an authentication cookie based on the user’s credentials. The daemons are written in Python for use with a Lightweight Directory Access Protocol (LDAP) authentication server (OpenLDAP or Microsoft Windows Active Directory 2003 and 2012).": "Ldap-auth overuje používateľov pristupujúcich k chráneným zdrojom cez proxy nginx. Obsahuje službu komunikujúcu s overovacím serverom a webovú službu, ktorá vytvára overovacie cookie podľa prihlasovacích údajov. Služby sú napísané v Pythone pre servery LDAP, napríklad OpenLDAP alebo Microsoft Windows Active Directory 2003 a 2012.", "Leave empty to auto-generate one.": "Ak chcete, aby sa automaticky vygeneroval, nechajte prázdne.", "Leave empty to keep every backup (not recommended).": "Ak chcete zachovať všetky zálohy, nechajte prázdne (neodporúča sa).", "Leave empty to use the retention of the storage.": "Ak chcete použiť uchovávanie úložiska, nechajte prázdne.", @@ -3524,24 +3527,24 @@ "Legend:": "Vysvetlivky:", "Let's review your current network configuration.": "Pozrime si aktuálne nastavenie siete.", "Liberate your videos and unleash infinite possibilities.": "Uvoľnite svoje videá a uvoľnite nekonečné možnosti.", - "Libraries: /data/media/movies, /data/media/series and /data/media/music. Select them in the media server.": "Knižnice: /data/media/mobiles, /data/media/series a /data/media/music. Vyberte ich na mediálnom serveri.", + "Libraries: /data/media/movies, /data/media/series and /data/media/music. Select them in the media server.": "Knižnice: /data/media/movies, /data/media/series a /data/media/music. Vyberte ich v mediálnom serveri.", "Library size in GB": "Veľkosť knižnice v GB", "LibreDB Studio": "LibreDB Studio", - "LibreOffice is a free and powerful office suite, and a successor to OpenOffice.org (commonly known as OpenOffice). Its clean interface and feature-rich tools help you unleash your creativity and enhance your productivity.": "LibreOffice je bezplatný a výkonný kancelársky apartmán a nástupca OpenOffice.org (bežne známy ako OpenOffice). Jeho čisté rozhranie a funkcie bohaté nástroje vám pomôžu uvoľniť vašu kreativitu a zvýšiť vašu produktivitu.", - "LibreWolf is a custom and independent version of Firefox, with the primary goals of privacy, security and user freedom. LibreWolf also aims to remove all the telemetry, data collection and annoyances, as well as disabling anti-freedom features like DRM.": "LibreWolf je vlastná a nezávislá verzia Firefox, s primárnymi cieľmi súkromia, bezpečnosti a užívateľskej slobody. Cieľom LibreWolf je tiež odstrániť všetky telemetriu, zber dát a otravy, ako aj znemožniť protislobodnosť funkcií ako DRM.", + "LibreOffice is a free and powerful office suite, and a successor to OpenOffice.org (commonly known as OpenOffice). Its clean interface and feature-rich tools help you unleash your creativity and enhance your productivity.": "LibreOffice je bezplatný a výkonný kancelársky balík a nástupca OpenOffice.org, známeho ako OpenOffice. Prehľadné rozhranie a bohaté nástroje podporujú tvorivosť a zvyšujú produktivitu.", + "LibreWolf is a custom and independent version of Firefox, with the primary goals of privacy, security and user freedom. LibreWolf also aims to remove all the telemetry, data collection and annoyances, as well as disabling anti-freedom features like DRM.": "LibreWolf je nezávislá upravená verzia Firefoxu zameraná na súkromie, bezpečnosť a slobodu používateľa. Odstraňuje telemetriu, zber údajov a rušivé prvky a vypína funkcie obmedzujúce používateľa, napríklad DRM.", "Librespeed is a very lightweight Speedtest implemented in Javascript, using XMLHttpRequest and Web Workers.": "Librespeed je veľmi ľahký Speedtest implementovaný v Javascripte, pomocou XMLHttpRequest a Web Workers.", - "Lidarr is a music collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new tracks from your favorite artists and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.": "Lidarr je manažérom hudobných kolekcií pre užívateľov Usenet a BitTorrent. To môže sledovať viac RSS kanálov pre nové stopy od svojich obľúbených umelcov a bude chytiť, triediť a premenovať ich. Môže byť tiež nakonfigurovaný tak, aby automaticky upgrade kvality súborov už stiahnutých, keď je k dispozícii kvalitnejší formát.", - "Lightweight Docker management UI": "Ľahké Docker Management UI", + "Lidarr is a music collection manager for Usenet and BitTorrent users. It can monitor multiple RSS feeds for new tracks from your favorite artists and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.": "Lidarr je správca hudobnej zbierky pre používateľov Usenetu a BitTorrentu. Sleduje RSS kanály s novými skladbami obľúbených interpretov, získava ich, triedi a premenúva. Dokáže tiež nahradiť už stiahnuté súbory kvalitnejšou verziou, keď sa objaví.", + "Lightweight Docker management UI": "Nenáročné rozhranie na správu Dockeru", "Likely cause: host directory permissions deny the container's mapped UID.": "Pravdepodobná príčina: oprávnenia priečinka na hostovi nepovoľujú namapované UID kontajnera.", "Limiting size and optimizing journald": "Obmedzujem veľkosť a optimalizujem journald", "Limiting size and optimizing journald...": "Obmedzujem veľkosť a optimalizujem journald...", - "Limnoria A robust, full-featured, and user/programmer-friendly Python IRC bot, with many existing plugins. Successor of the well-known Supybot.": "Limnoria Robustný, full-featured, a užívateľsky / programátor-friendly Python IRC robot, s mnohými existujúcimi modulmi. Nástupca známeho Supybota.", - "Limnoria joins no IRC network until its configuration file exists. Create it with the setup wizard from the Proxmox host: pct exec -- bash -c 'cd /config && limnoria-wizard'": "Limnoria sa pripája k žiadnej sieti IRC, kým neexistuje jej konfiguračný súbor. Vytvorte ho pomocou sprievodcu nastavením z Proxmox hostiteľa: pct exec -- bash -c 'cd /config && limnoria-wizard'", + "Limnoria A robust, full-featured, and user/programmer-friendly Python IRC bot, with many existing plugins. Successor of the well-known Supybot.": "Limnoria je robustný a funkčne bohatý IRC robot v Pythone, prívetivý pre používateľov aj programátorov. Ponúka mnoho doplnkov a je nástupcom známeho Supybota.", + "Limnoria joins no IRC network until its configuration file exists. Create it with the setup wizard from the Proxmox host: pct exec -- bash -c 'cd /config && limnoria-wizard'": "Limnoria sa nepripojí k žiadnej sieti IRC, kým neexistuje konfiguračný súbor. Vytvorte ho sprievodcom spusteným z hostiteľa Proxmox: pct exec -- bash -c 'cd /config && limnoria-wizard'", "Line to paste (single line, including \"command=...\" prefix):": "Riadok na vloženie (jeden riadok vrátane predpony \"command=...\"):", "Linux Installation Options": "Možnosti inštalácie Linuxu", "Linux/Mac path:": "Cesta pre Linux/Mac:", "LinuxServer Jellyfin with optional GPU passthrough": "LinuxServer Jellyfin s voliteľným GPU priechodom", - "LinuxServer MariaDB requires a user, database and password": "LinuxServer MariaDB requires a user, databáza a heslo", + "LinuxServer MariaDB requires a user, database and password": "LinuxServer MariaDB vyžaduje používateľa, databázu a heslo", "List Available Disks": "Zobraziť dostupné disky", "List IOMMU group mapping": "Zobraziť mapovanie IOMMU skupín", "List NVMe devices": "Zobraziť NVMe zariadenia", @@ -3567,7 +3570,7 @@ "Listening on:": "Počúva na:", "Listening ports:": "Počúvajúce porty:", "Listing relevant CT users and their mapped UID/GID on host...": "Zobrazujem relevantných používateľov CT a ich namapované UID/GID na hostovi...", - "Load and verify the WireGuard module on the Proxmox host": "Načítať a overiť WireGuard modul na Proxmox hostiteľa", + "Load and verify the WireGuard module on the Proxmox host": "Načítať a overiť modul WireGuard na hostiteľovi Proxmox", "Loading modules...": "Načítavam moduly...", "Loading the Helper-Scripts catalog...": "Načítava sa katalóg Helper-Scripts...", "Loading the host kernel module:": "Načítavam modul hostiteľského jadra:", @@ -3581,7 +3584,7 @@ "Local keyfile is missing but a recovery copy was found in PBS.": "Lokálny keyfile chýba, ale v PBS sa našla obnovovacia kópia.", "Local network only (192.168.0.0/16)": "Iba lokálna sieť (192.168.0.0/16)", "Local restore error log": "Záznam chyby lokálnej obnovy", - "Local storage for PostgreSQL": "Lokálne skladovanie pre PostgreSQL", + "Local storage for PostgreSQL": "Lokálne úložisko pre PostgreSQL", "Locale generated": "Locale bolo vytvorené", "Location:": "Umiestnenie:", "Log": "Záznam", @@ -3600,7 +3603,7 @@ "Logout from {registry} timed out. Saved file credentials were not changed.": "Pri odhlasovaní z {registry} uplynul časový limit. Prihlasovacie údaje uložené v súbore sa nezmenili.", "Logrotate optimization completed": "Optimalizácia logrotate je dokončená", "Logrotate service restarted successfully": "Služba logrotate bola úspešne reštartovaná", - "Lollypop is a lightweight modern music player designed to work excellently on the GNOME desktop environment.": "Lollypop je ľahký moderný hudobný prehrávač navrhnutý tak, aby pracoval vynikajúco na GNOME desktop prostredí.", + "Lollypop is a lightweight modern music player designed to work excellently on the GNOME desktop environment.": "Lollypop je nenáročný moderný hudobný prehrávač navrhnutý pre pracovné prostredie GNOME.", "Long Test — Background": "Dlhý test - na pozadí", "Long self-test started on": "Dlhý samo-test bol spustený na", "Long test — full scan, runs in background if closed": "Dlhý test - úplná kontrola, beží na pozadí aj po zatvorení", @@ -3611,7 +3614,7 @@ "Low free space warning": "Upozornenie na málo voľného miesta", "Low-code programming for event-driven applications": "Nízkokódové programovanie pre aplikácie riadené udalosťami", "Low-power CPU platform": "Úsporná CPU platforma", - "Luanti (formerly Minetest) is an open source voxel game-creation platform with easy modding and game creation": "Luanti (predtým Minetest) je open source voxel-creation platforma s jednoduchým moding a tvorba hry", + "Luanti (formerly Minetest) is an open source voxel game-creation platform with easy modding and game creation": "Luanti (predtým Minetest) je platforma s otvoreným zdrojovým kódom na tvorbu voxelových hier s jednoduchou podporou úprav a tvorby hier", "Lucky web interface": "Webové rozhranie Lucky", "Lychee is a free photo-management tool, which runs on your server or web-space. Installing is a matter of seconds. Upload, manage and share photos like from a native application. Lychee comes with everything you need and all your photos are stored securely.": "Lychee je bezplatný nástroj na správu fotografií, ktorý beží na vašom serveri alebo webovom priestore. Inštalácia je otázka sekúnd. Nahrajte, spravujte a zdieľajte fotografie ako z natívnej aplikácie. Lychee prichádza so všetkým, čo potrebujete a všetky vaše fotografie sú uložené bezpečne.", "Lynis - Security Audit": "Lynis - bezpečnostná kontrola", @@ -3678,7 +3681,7 @@ "Manual review is required.": "Vyžaduje sa manuálna kontrola.", "Manual steps recommended after import": "Odporúčané ručné kroky po importe", "Manual upgrade guide step by step": "Ručný sprievodca aktualizáciou krok za krokom", - "Manyfold is an open source, self-hosted web application for managing a collection of 3D models, particularly focused on 3D printing.": "Manyfold je open source, self-hosted webová aplikácia pre správu kolekcie 3D modelov, najmä zamerané na 3D tlač.", + "Manyfold is an open source, self-hosted web application for managing a collection of 3D models, particularly focused on 3D printing.": "Manyfold je webová aplikácia s otvoreným zdrojovým kódom na vlastnom serveri na správu zbierky 3D modelov, najmä pre 3D tlač.", "Mapped GID on host": "GID namapované na serveri", "Mapped UID on host": "UID namapované na serveri", "MariaDB has no web interface: applications and clients connect to port 3306 of this container.": "MariaDB nemá webové rozhranie: aplikácie a klienti sa pripájajú na port 3306 tohto kontajnera.", @@ -3691,12 +3694,12 @@ "Maximum auto-repair attempts reached (3). Please review the log and run any remaining commands manually.": "Bol dosiahnutý maximálny počet automatických opráv (3). Skontrolujte záznam a zostávajúce príkazy spustite ručne.", "May need to restart terminal": "Možno bude potrebné reštartovať terminál", "Media & Streaming": "Médiá a streaming", - "Media discovery and request management for Jellyfin, Plex and Emby.": "Mediálny prieskum a správa požiadaviek pre Jellyfin, Plex a Emby.", - "Media library transcoding and health checking, with an internal worker node.": "Média knižnica transkódovanie a zdravotná kontrola, s interným uzol pracovníka.", + "Media discovery and request management for Jellyfin, Plex and Emby.": "Vyhľadávanie médií a správa požiadaviek pre Jellyfin, Plex a Emby.", + "Media library transcoding and health checking, with an internal worker node.": "Prevod formátov a kontrola stavu mediálnej knižnice s interným pracovným uzlom.", "Media server": "Mediálny server", "Media server selection cancelled or invalid": "Výber mediálneho servera zrušený alebo neplatný", - "MediaElch is a MediaManager for Kodi. Information about Movies, TV Shows, Concerts and Music are stored as nfo files. Fanarts are downloaded automatically from fanart.tv. Using the nfo generator, MediaElch can be used with other MediaCenters as well.": "MediaElch je MediaManager pre Kodi. Informácie o filmoch, TV Shows, koncertoch a hudbe sú uložené ako nfo súbory. Fanarty sa automaticky sťahujú z fanart.tv. Pomocou nFo generátora, MediaElch môžu byť použité s inými MediaCenters rovnako.", - "Medusa is an automatic Video Library Manager for TV Shows. It watches for new episodes of your favorite shows, and when they are posted it does its magic.": "Medusa je automatický manažér videoknižnice pre TV show. Sleduje nové epizódy vašich obľúbených šou, a keď sú zverejnené to robí jeho mágiu.", + "MediaElch is a MediaManager for Kodi. Information about Movies, TV Shows, Concerts and Music are stored as nfo files. Fanarts are downloaded automatically from fanart.tv. Using the nfo generator, MediaElch can be used with other MediaCenters as well.": "MediaElch je správca médií pre Kodi. Informácie o filmoch, seriáloch, koncertoch a hudbe ukladá do súborov nfo. Grafiku automaticky sťahuje z fanart.tv. Vďaka generovaniu nfo ho možno používať aj s inými mediálnymi centrami.", + "Medusa is an automatic Video Library Manager for TV Shows. It watches for new episodes of your favorite shows, and when they are posted it does its magic.": "Medusa automaticky spravuje knižnicu televíznych seriálov. Sleduje nové epizódy obľúbených seriálov a spracuje ich, keď sú dostupné.", "Memory": "Pamäť", "Memory in MB": "Pamäť v MB", "Memory optimization completed.": "Optimalizácia pamäte je dokončená.", @@ -3704,18 +3707,18 @@ "Memory restored.": "Pamäť obnovená.", "Memory settings optimized successfully": "Nastavenie pamäte bolo úspešne optimalizované", "Memory:": "Pamäť:", - "Memos is a lightweight, self-hosted memo hub. Open Source and Free forever.": "Memos je ľahký, self-hosted memo centrum. Open Source a Free navždy.", + "Memos is a lightweight, self-hosted memo hub. Open Source and Free forever.": "Memos je nenáročné centrum poznámok na vlastnom serveri. Má otvorený zdrojový kód a je navždy bezplatné.", "Messaging & Notifications": "Správy a upozornenia", "Messaging & Queues": "Správy a fronty", - "Messenger for the Decentralized Web": "Posol pre decentralizovaný web", + "Messenger for the Decentralized Web": "Komunikačná aplikácia pre decentralizovaný web", "Method:": "Spôsob:", - "Microsoft Edge is a cross-platform web browser developed by Microsoft and based on Chromium.": "Microsoft Edge je cross-platform webový prehliadač vyvinutý spoločnosťou Microsoft a založený na Chromium.", + "Microsoft Edge is a cross-platform web browser developed by Microsoft and based on Chromium.": "Microsoft Edge je multiplatformový webový prehliadač vyvíjaný Microsoftom a založený na Chromiu.", "Migrate VMs away from node being upgraded": "Presuňte VM mimo uzol, ktorý sa aktualizuje", "Migrate away any guests that must keep running": "Presuňte mimo uzla všetkých hostí, ktorí musia zostať bežať", "Migrated": "Migrované", "Migrated legacy ProxMenux NVIDIA blacklist state — module will reload after reboot": "Starý stav ProxMenux NVIDIA blacklistu bol migrovaný - modul sa znovu načíta po reštarte", "MineOS web interface": "Webové rozhranie MineOS", - "Minisatip is a multi-threaded satip server version 1.2 that runs under Linux and it was tested with DVB-S, DVB-S2, DVB-T, DVB-T2, DVB-C, DVB-C2, ATSC and ISDB-T cards.": "Minisatip je viacväzbová satipová verzia 1.2, ktorá beží pod Linuxom a bola testovaná pomocou DVB-S, DVB-S2, DVB-T, DVB-T2, DVB-C, DVB-C2, ATSC a ISDB-T kariet.", + "Minisatip is a multi-threaded satip server version 1.2 that runs under Linux and it was tested with DVB-S, DVB-S2, DVB-T, DVB-T2, DVB-C, DVB-C2, ATSC and ISDB-T cards.": "Minisatip je viacvláknový server SAT>IP verzie 1.2 pre Linux. Bol testovaný s kartami DVB-S, DVB-S2, DVB-T, DVB-T2, DVB-C, DVB-C2, ATSC a ISDB-T.", "Mirror URL not available for this script.": "Záložná adresa pre tento skript nie je dostupná.", "Miscellaneous": "Rôzne", "Missing": "Chýba", @@ -3742,7 +3745,7 @@ "Modifying Fastfetch configuration...": "Upravujem nastavenie Fastfetch...", "Modules configuration updated.": "Nastavenie modulov bolo aktualizované.", "Modules loaded.": "Moduly boli načítané.", - "MongoDB 4.4, the last series that runs on a CPU without AVX.": "MongoDB 4.4, posledná séria beží na CPU bez AVX.", + "MongoDB 4.4, the last series that runs on a CPU without AVX.": "MongoDB 4.4 je posledný rad, ktorý funguje na CPU bez AVX.", "Monica is an open source personal relationship management system, that lets you document your life.": "Monica je open source systém riadenia osobných vzťahov, ktorý vám umožní dokumentovať svoj život.", "Monitor Activated": "Monitor je zapnutý", "Monitor Deactivated": "Monitor je vypnutý", @@ -3779,11 +3782,11 @@ "Mount Samba Share on Host": "Pripojiť Samba zdieľanie na hostovi", "Mount USB disk?": "Pripojiť USB disk?", "Mount a USB drive now": "Pripojiť USB disk teraz", - "Mount activation cancelled": "Aktivácia montáže zrušená", + "Mount activation cancelled": "Aktivácia pripojenia bola zrušená", "Mount all datasets": "Pripojiť všetky datasety", "Mount already exists for this path in container": "Pre túto cestu už v kontajneri mount existuje", "Mount and persist with UUID:": "Pripojiť a uložiť natrvalo pomocou UUID:", - "Mount configuration cancelled": "Konfigurácia montáže zrušená", + "Mount configuration cancelled": "Nastavenie pripojenia bolo zrušené", "Mount failed": "Pripojenie zlyhalo", "Mount mode applied": "Použitý režim montáže", "Mount name": "Názov pripojenia", @@ -3808,7 +3811,7 @@ "Mount status:": "Stav pripojenia:", "Mount this device and use it as the backup destination?": "Pripojiť toto zariadenie a použiť ho ako cieľ záloh?", "Mount was busy — performed lazy unmount": "Mount sa používal - vykonalo sa oneskorené odpojenie (lazy unmount)", - "Mount your cloud drive on your home NAS": "Namontujte svoju cloudovú jazdu na váš domov NAS", + "Mount your cloud drive on your home NAS": "Pripojte cloudový disk k domácemu NAS", "Mounted": "Pripojené", "Mounted ISO on device": "ISO bolo pripojené k zariadeniu", "Mounted at": "Pripojené v", @@ -3833,10 +3836,10 @@ "Multiple networks or external networks are not yet supported": "Viaceré siete alebo externé siete zatiaľ nie sú podporované", "Multiple recovery groups found in PBS. Pick the one that originally created the keyfile:": "V PBS sa našlo viac obnovovacích skupín. Vyberte tú, ktorá pôvodne vytvorila keyfile:", "Multiple rootfs directories were found in this archive. Restore cannot continue automatically.": "V tomto archíve sa našlo viac priečinkov rootfs. Automatická obnova nemôže pokračovať.", - "Music Collection and Streaming Server": "Zber hudby a streamovanie servera", + "Music Collection and Streaming Server": "Správca hudobnej zbierky a streamovací server", "Music software that transforms your listening experience": "Hudobný softvér, ktorý mení vaše počúvanie", "MySQL Workbench is a unified visual tool for database architects, developers, and DBAs. MySQL Workbench provides data modeling, SQL development, and comprehensive administration tools for server configuration, user administration, backup, and much more.": "MySQL Workbench je jednotný vizuálny nástroj pre databázových architektov, vývojárov a DBA. MySQL Workbench poskytuje dátové modelovanie, vývoj SQL a komplexné nástroje správy pre konfiguráciu serverov, správu užívateľov, zálohovanie a oveľa viac.", - "Mylar3 is an automated Comic Book downloader (cbr/cbz) for use with NZB and torrents written in python. It supports SABnzbd, NZBGET, and many torrent clients in addition to DDL.": "Mylar3 je automatický downloader Comic Book (cbr/cbz) pre použitie s NZB a torrenty napísané v pythone. Podporuje SABnzbd, NZBGET, a mnoho torrentových klientov okrem DDL.", + "Mylar3 is an automated Comic Book downloader (cbr/cbz) for use with NZB and torrents written in python. It supports SABnzbd, NZBGET, and many torrent clients in addition to DDL.": "Mylar3 je automatický nástroj na sťahovanie komiksov (cbr/cbz) cez NZB a torrenty, napísaný v Pythone. Podporuje SABnzbd, NZBGET, mnohých torrentových klientov aj priame sťahovanie (DDL).", "NAS Systems": "NAS systémy", "NETWORK CONFIGURATION ANALYSIS": "ANALÝZA NASTAVENIA SIETE", "NFS Access Restricted": "NFS prístup je obmedzený", @@ -3900,7 +3903,7 @@ "NICs:": "Sieťové karty:", "NOT FOUND": "NENÁJDENÉ", "NOTE: The host directory and its contents will remain unchanged.": "POZNÁMKA: priečinok na hostovi ani jeho obsah sa nezmenia.", - "NUT server user for clients such as PeaNUT": "Používateľ NUT server pre klientov ako PeaNUT", + "NUT server user for clients such as PeaNUT": "Používateľ servera NUT pre klientov ako PeaNUT", "NVENC patch detected — list narrowed to versions supported by keylase/nvidia-patch.": "Zistil sa NVENC patch - zoznam je zúžený na verzie podporované cez keylase/nvidia-patch.", "NVIDIA (CUDA)": "NVIDIA (CUDA)", "NVIDIA (CUDA; official GPU image)": "NVIDIA (CUDA; oficiálny obraz GPU)", @@ -3970,7 +3973,7 @@ "NVMe skipped (to add as PCIe use 'Add Controller or NVMe PCIe to VM'):": "NVMe preskočené (ak ho chcete pridať ako PCIe, použite 'Pridať radič alebo NVMe PCIe do VM'):", "NVMe-specific SMART log": "SMART záznam špecifický pre NVMe", "NVR & Cameras": "NVR a kamery", - "NVR with optional VA-API video acceleration and hardware object detectors": "NVR s voliteľným video zrýchlením VA-API a detektormi hardvérových objektov", + "NVR with optional VA-API video acceleration and hardware object detectors": "Sieťový videorekordér s voliteľným zrýchlením videa cez VA-API a hardvérovými detektormi objektov", "NZBGet web interface": "Webové rozhranie NZBGet", "Name": "Názov", "Name for this application": "Názov tejto aplikácie", @@ -3981,13 +3984,13 @@ "Name of the database created on the first start": "Názov databázy vytvorenej pri prvom štarte", "Name of the database on the MariaDB server": "Názov databázy na serveri MariaDB", "Name of the directory to share (lowercase letters, digits, - or _)": "Názov adresára na zdieľanie (malé písmená, číslice, - alebo _)", - "Name of the internal worker node": "Názov vnútorného uzol pracovníka", + "Name of the internal worker node": "Názov interného pracovného uzla", "Name of this wallabag instance, shown in the interface and in 2FA codes": "Názov tejto inštancie wallabag, zobrazený v rozhraní a v kódoch 2FA", "Name or address NetBox will be opened at (for example netbox.example.com)": "Názov alebo adresa, na ktorej bude NetBox dostupný (napríklad netbox.example.com)", - "Name or part of the description of the application": "Názov alebo časť opisu žiadosti", + "Name or part of the description of the application": "Názov alebo časť popisu aplikácie", "Name or part of the option or of the application": "Názov alebo časť možnosti alebo aplikácie", "Name:": "Názov:", - "Named accounts need private mode. Stop the container, set public: false in /config/config.js, start it again and create each user with: pct exec -- env THELOUNGE_HOME=/config s6-setuidgid abc thelounge add ": "Menované účty potrebujú súkromný režim. Zastaviť kontajner, nastaviť verejnosť: false in /config/config.js, začať znova a vytvoriť každého užívateľa s: pct exec -- env THELOUNGE_HOME=/config s6-setuid abc thelounge pridať ", + "Named accounts need private mode. Stop the container, set public: false in /config/config.js, start it again and create each user with: pct exec -- env THELOUNGE_HOME=/config s6-setuidgid abc thelounge add ": "Používateľské účty vyžadujú súkromný režim. Zastavte kontajner, v /config/config.js nastavte public: false, znova ho spustite a každého používateľa vytvorte príkazom: pct exec -- env THELOUNGE_HOME=/config s6-setuidgid abc thelounge add ", "Needed:": "Potrebné:", "Needs": "Vyžaduje", "Neither /etc/kernel/cmdline nor /etc/default/grub found.": "Nenašiel sa /etc/kernel/cmdline ani /etc/default/grub.", @@ -3996,7 +3999,7 @@ "NetBIOS Service: STOPPED": "Služba NetBIOS: ZASTAVENÁ", "NetBIOS port 139:": "NetBIOS port 139:", "NetBox": "NetBox", - "Netbox is an IP address management (IPAM) and data center infrastructure management (DCIM) tool. Initially conceived by the network engineering team at DigitalOcean, NetBox was developed specifically to address the needs of network and infrastructure engineers. It is intended to function as a domain-specific source of truth for network operations.": "Netbox je nástroj riadenia IP adresy (IPAM) a riadenia infraštruktúry dátových centier (DCIM). Pôvodne koncipovaný sieťovým tímom v DigitalOcean, bol NetBox vyvinutý špeciálne pre potreby sieťových a infraštruktúrnych inžinierov. Je určený na fungovanie ako doménový špecifický zdroj pravdy pre siete operations.", + "Netbox is an IP address management (IPAM) and data center infrastructure management (DCIM) tool. Initially conceived by the network engineering team at DigitalOcean, NetBox was developed specifically to address the needs of network and infrastructure engineers. It is intended to function as a domain-specific source of truth for network operations.": "NetBox je nástroj na správu IP adries (IPAM) a infraštruktúry dátových centier (DCIM). Pôvodne ho navrhol sieťový tím spoločnosti DigitalOcean pre potreby správcov sietí a infraštruktúry. Slúži ako autoritatívny zdroj údajov pre správu siete.", "Network": "Sieť", "Network :": "Sieť :", "Network & Firewall": "Sieť a firewall", @@ -4016,7 +4019,7 @@ "Network Restart Failed": "Reštart siete zlyhal", "Network Restarted": "Sieť bola reštartovaná", "Network Tools": "Sieťové nástroje", - "Network UPS Tools server for a UPS connected to the host by USB.": "Sieťový server UPS Tools pre UPS pripojený k hostiteľovi cez USB.", + "Network UPS Tools server for a UPS connected to the host by USB.": "Server Network UPS Tools pre UPS pripojený k hostiteľovi cez USB.", "Network access:": "Sieťový prístup:", "Network bridge": "Sieťový most", "Network configuration backed up": "Nastavenie siete bolo zálohované", @@ -4040,7 +4043,7 @@ "NetworkManager is running (may cause conflicts)": "NetworkManager beží (môže spôsobovať konflikty)", "NetworkManager is running, which may conflict with Proxmox.": "NetworkManager beží a môže byť v konflikte s Proxmoxom.", "NetworkManager not running": "NetworkManager nebeží", - "Networks the peers reach through the tunnel (0.0.0.0/0 = all traffic)": "Siete, ktoré rovesníci dosahujú tunelom (0,0.0.0/0 = všetka doprava)", + "Networks the peers reach through the tunnel (0.0.0.0/0 = all traffic)": "Siete dostupné účastníkom cez tunel (0.0.0.0/0 = všetka prevádzka)", "New Folder in /mnt": "Nový priečinok v /mnt", "New Group": "Nová skupina", "New Search": "Nové hľadanie", @@ -4064,10 +4067,10 @@ "Nextcloud configuration cancelled": "Konfigurácia Nextcloud zrušená", "Nextcloud gives you access to all your files wherever you are.": "Nextcloud vám umožní prístup ku všetkým súborom kdekoľvek ste.", "Nextcloud volume size in GB": "Veľkosť dátového zväzku Nextcloud v GB", - "Nextcloud with private PostgreSQL and Redis dependencies": "Nextcloud s súkromnými PostgreSQL a Redis závislé", - "Nginx is an HTTP web server, reverse proxy, content cache, load balancer, TCP/UDP proxy server, and mail proxy server.": "Nginx je webový server HTTP, reverzný proxy, vyrovnávacia pamäť obsahu, načítací balander, proxy server TCP/UDP a proxy server pošty.", - "Nginx webserver and reverse proxy with php support and a built-in Certbot (Let's Encrypt) client. It also contains fail2ban for intrusion prevention.": "Nginx webserver a reverzný proxy s php podporou a vstavaný Certbot (Let's Encrypt) klient. Obsahuje aj fail2ban na prevenciu vniknutia.", - "Ngircd is a free, portable and lightweight Internet Relay Chat server for small or private networks, developed under the GNU General Public License (GPL). It is easy to configure, can cope with dynamic IP addresses, and supports IPv6, SSL-protected connections as well as PAM for authentication. It is written from scratch and not based on the original IRCd.": "Ngircd je bezplatný, prenosný a ľahký internetový server Relay Chat pre malé alebo súkromné siete, vyvinutý podľa GNU General Public License (GPL). Je ľahko konfigurovateľný, dokáže sa vyrovnať s dynamickými IP adresami a podporuje pripojenie chránené IPv6, SSL, ako aj PAM pre autentifikáciu. Je napísaný od nuly a nie je založený na pôvodnom IRCd.", + "Nextcloud with private PostgreSQL and Redis dependencies": "Nextcloud so samostatnými internými službami PostgreSQL a Redis", + "Nginx is an HTTP web server, reverse proxy, content cache, load balancer, TCP/UDP proxy server, and mail proxy server.": "Nginx je webový server HTTP, reverzný proxy, vyrovnávacia pamäť obsahu, nástroj na rozkladanie záťaže, proxy server TCP/UDP a poštový proxy server.", + "Nginx webserver and reverse proxy with php support and a built-in Certbot (Let's Encrypt) client. It also contains fail2ban for intrusion prevention.": "Webový server a reverzný proxy Nginx s podporou PHP a vstavaným klientom Certbot (Let's Encrypt). Obsahuje aj fail2ban na ochranu pred prienikom.", + "Ngircd is a free, portable and lightweight Internet Relay Chat server for small or private networks, developed under the GNU General Public License (GPL). It is easy to configure, can cope with dynamic IP addresses, and supports IPv6, SSL-protected connections as well as PAM for authentication. It is written from scratch and not based on the original IRCd.": "NgIRCd je bezplatný, prenosný a nenáročný server IRC pre malé alebo súkromné siete, vydaný pod licenciou GNU GPL. Ľahko sa nastavuje, pracuje s dynamickými IP adresami a podporuje IPv6, spojenia chránené SSL aj overovanie cez PAM. Bol vytvorený od základu a nevychádza z pôvodného IRCd.", "No": "Nie", "No .ova or .ovf files found in:": "V tomto priečinku sa nenašli žiadne .ova ani .ovf súbory:", "No .ovf descriptor found inside OVA.": "V OVA sa nenašiel žiadny .ovf popisovač.", @@ -4192,7 +4195,7 @@ "No available Controllers/NVMe devices were found.": "Nenašli sa žiadne dostupné Controller/NVMe zariadenia.", "No available disks found.": "Nenašli sa žiadne dostupné disky.", "No backup found, logrotate configuration not changed": "Nenašla sa žiadna záloha, nastavenie logrotate sa nemenilo", - "No backup is scheduled: the rsnapshot lines in /config/crontabs/root are commented out. Uncomment or adjust the intervals you want, then restart the container.": "Nie je naplánovaná žiadna záloha: linky rsnapshot v /config/crontabs/root sú komentované. Odpojte alebo nastavte intervaly, ktoré chcete, potom reštartujte kontajner.", + "No backup is scheduled: the rsnapshot lines in /config/crontabs/root are commented out. Uncomment or adjust the intervals you want, then restart the container.": "Žiadna záloha nie je naplánovaná: riadky rsnapshot v /config/crontabs/root sú zakomentované. Odkomentujte alebo upravte požadované intervaly a potom kontajner reštartujte.", "No backups": "Žiadne zálohy", "No backups found": "Nenašli sa žiadne zálohy", "No bridge configuration issues found": "V nastavení bridge sa nenašli problémy", @@ -4308,7 +4311,7 @@ "No scheduled backup jobs configured.": "Nie sú nastavené žiadne naplánované úlohy záloh.", "No scheduled backup jobs found.": "Nenašli sa žiadne naplánované úlohy zálohy.", "No scripts found for:": "Nenašli sa žiadne skripty pre:", - "No security relaxation is required for the reviewed profile.": "Žiadny bezpečnostný relax je required pre preskúmaný profil.", + "No security relaxation is required for the reviewed profile.": "Overený profil nevyžaduje uvoľnenie bezpečnostných obmedzení.", "No selectable USB device was found. Type the host node by hand.": "Nenašlo sa žiadne zariadenie USB, ktoré možno vybrať. Zadajte uzol hostiteľa ručne.", "No self-test history found for": "Nenašla sa história samo-testov pre", "No self-test log available for": "Nie je dostupný log samo-testu pre", @@ -4375,9 +4378,9 @@ "Not mounted": "Nepripojené", "Not portable:": "Neprenáša sa:", "Not registered as Proxmox storage — use 'LXC Mount Manager' to bind-mount": "Nie je zaregistrované ako úložisko Proxmoxu - použite 'Správca LXC mountov' na bind mount", - "Not required (access code only)": "Nie je required (len prístupový kód)", - "Not required (password only)": "Nie je required (len heslo)", - "Not required (token only)": "Nie je required (iba token)", + "Not required (access code only)": "Nevyžaduje sa (iba prístupový kód)", + "Not required (password only)": "Nevyžaduje sa (iba heslo)", + "Not required (token only)": "Nevyžaduje sa (iba token)", "Not yet verified by ProxMenux (beta)": "Zatiaľ overené ProxMenux (beta)", "Note: A system reboot will be required after enabling IOMMU.": "Poznámka: po zapnutí IOMMU bude potrebný reštart systému.", "Note: this only works if the NFS server does NOT use 'all_squash' for root.": "Poznámka: funguje to iba vtedy, ak NFS server NEPOUŽÍVA 'all_squash' pre root.", @@ -4397,7 +4400,7 @@ "Nothing was changed.": "Nič sa nezmenilo.", "Nouveau module is loaded, attempting to unload...": "Modul Nouveau je načítaný, skúšam ho odpojiť...", "Number of CPU cores (default: 2)": "Počet CPU jadier (predvolené: 2)", - "Nzbget is a usenet downloader, written in C++ and designed with performance in mind to achieve maximum download speed by using very little system resources.": "Nzbget je usenet downloader, napísaný v C++ a navrhnutý s výkonom na dosiahnutie maximálnej rýchlosti sťahovania pomocou veľmi málo systémových zdrojov.", + "Nzbget is a usenet downloader, written in C++ and designed with performance in mind to achieve maximum download speed by using very little system resources.": "NZBGet je nástroj na sťahovanie z Usenetu napísaný v C++. Je navrhnutý na maximálnu rýchlosť s minimálnymi nárokmi na systémové zdroje.", "Nzbhydra2 is a meta search application for NZB indexers, the spiritual successor to NZBmegasearcH, and an evolution of the original application NZBHydra.": "Nzbhydra2 je meta vyhľadávacia aplikácia pre NZB indexers, duchovný nástupca NZBmegasearcH, a vývoj pôvodnej aplikácie NZBHydra.", "OCI application": "Aplikácia OCI", "OCI container, configuration kept. Remove its GPU from OCI manager Apps, with Recreate: CT": "Kontajner OCI, konfigurácia zachovaná. Odstráňte jeho GPU z OCI manager Apps pomocou Znovu vytvoriť: CT", @@ -4409,7 +4412,7 @@ "OCI management could not be completed. Check the backend status; no additional cleanup has been authorized.": "Správu OCI aplikácií nebolo možné dokončiť. Skontrolujte stav backendu; žiadne ďalšie vyčistenie nebolo povolené.", "OCI manager Apps (Technology Preview)": "Správca OCI aplikácií (Technology Preview)", "OCI manager Apps (beta)": "Správca OCI aplikácií (beta)", - "OCI manager Apps is a Technology Preview: if something does not work as expected, please report it on GitHub with the application name.": "OCI manager Apps je Technology Preview: ak niečo nefunguje podľa očakávania, nahláste to na GitHub s názvom aplikácie.", + "OCI manager Apps is a Technology Preview: if something does not work as expected, please report it on GitHub with the application name.": "Správca OCI aplikácií je v skúšobnej fáze: ak niečo nefunguje podľa očakávania, nahláste to na GitHube spolu s názvom aplikácie.", "OCI manager Apps is a beta: if something does not work as expected, please report it on GitHub with the application name.": "Správca OCI aplikácií je beta: ak niečo nefunguje podľa očakávania, nahláste to na GitHub spolu s názvom aplikácie.", "OCI metadata integrity mismatch": "Nepomer integrity metaúdajov OCI", "OCI metadata too large": "Metaúdaje OCI príliš veľké", @@ -4417,7 +4420,7 @@ "OCI verification failed:": "Overenie OCI zlyhalo:", "OCR language (Tesseract code)": "Jazyk OCR (kód Tesseract)", "OK": "OK", - "ONLYOFFICE provides a full range of tools to create, edit and collaborate on text documents, spreadsheets, presentations, PDF forms and regular PDF files on web, desktop and mobile platforms.": "ONLYOFFICE poskytuje celý rad nástrojov na tvorbu, úpravu a spoluprácu na textových dokumentoch, tabuľkách, prezentáciách, PDF formulároch a pravidelných PDF súboroch na webových, stolových a mobilných platformách.", + "ONLYOFFICE provides a full range of tools to create, edit and collaborate on text documents, spreadsheets, presentations, PDF forms and regular PDF files on web, desktop and mobile platforms.": "ONLYOFFICE ponúka nástroje na tvorbu, úpravu a spoločnú prácu na textových dokumentoch, tabuľkách, prezentáciách, formulároch PDF aj bežných súboroch PDF na webe, počítačoch a mobilných zariadeniach.", "OR add new PVE 9 no-subscription repository:": "ALEBO pridajte nový PVE 9 no-subscription repozitár:", "OS hint:": "Odhad OS:", "OS release details": "Podrobnosti o vydaní systému", @@ -4434,18 +4437,18 @@ "Object detector": "Detektor objektov", "Object detector for Frigate": "Detektor objektov pre Frigate", "Observer is not responding on port 4357": "Pozorovateľ nereaguje na prístav 4357", - "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian je note-prijímajúce aplikácie, ktoré vám umožní vytvoriť, odkaz, a usporiadať poznámky na vašom zariadení, so stovkami pluginov a tém prispôsobiť svoje pracovné postupy. Môžete tiež zverejniť svoje poznámky on-line, prístup je offline, a synchronizovať ich bezpečne s end-to-end šifrovanie.", + "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian umožňuje vytvárať, prepájať a usporadúvať poznámky na vašom zariadení. Stovky doplnkov a tém prispôsobia pracovný postup. Poznámky môžete publikovať online, používať offline a bezpečne synchronizovať s koncovým šifrovaním.", "Offer as exit node?": "Ponúknuť ako exit node?", - "Official Duplicati image: encrypted, incremental backups to local, network and cloud storage.": "Oficiálny image Duplicati: šifrované prírastkové zálohy do lokálneho, sieťového aj cloudového úložiska.", + "Official Duplicati image: encrypted, incremental backups to local, network and cloud storage.": "Oficiálny obraz Duplicati: šifrované prírastkové zálohy do lokálneho, sieťového aj cloudového úložiska.", "Official Emby Media Server image with optional VA-API or NVIDIA acceleration.": "Oficiálny obraz Emby Media Server s voliteľným zrýchlením VA-API alebo NVIDIA.", "Official Jellyfin image with optional VA-API or NVIDIA acceleration.": "Oficiálny obraz Jellyfin s voliteľným zrýchlením VA-API alebo NVIDIA.", "Official Linux Distributions": "Oficiálne Linux distribúcie", - "Official Plex Media Server image with optional hardware transcoding.": "Oficiálny obraz Plex Media Server s voliteľným transkódovaním hardvéru.", - "Official image": "Oficiálny image", + "Official Plex Media Server image with optional hardware transcoding.": "Oficiálny obraz Plex Media Server s voliteľným hardvérovým prevodom videa.", + "Official image": "Oficiálny obraz", "Old debian.sources file removed to prevent duplication": "Starý debian.sources súbor odstránený, aby nevznikla duplicita", "Old memory configuration detected. Replacing with balanced optimization...": "Našlo sa staršie nastavenie pamäte. Nahrádzam ho vyváženou optimalizáciou...", "Old time services removed successfully": "Staré časové služby boli úspešne odstránené", - "Ombi allows you to host your own Plex Request and user management system.": "Ombi vám umožní hostiť svoj vlastný Plex Žiadosť a systém správy užívateľov.", + "Ombi allows you to host your own Plex Request and user management system.": "Ombi umožňuje prevádzkovať vlastný systém na správu používateľov a požiadaviek na obsah pre Plex.", "On a privileged CT the mount options carry the only permissions.": "Pri privilegovanom CT určujú oprávnenia hlavne samotné možnosti pripojenia.", "On some systems, when starting the VM the host may slow down for several minutes until it stabilizes, or freeze completely.": "Na niektorých systémoch môže server pri štarte VM na niekoľko minút výrazne spomaliť, kým sa ustáli, alebo úplne zamrznúť.", "On the Borg server, append the following line to:": "Na Borg serveri pridajte nasledujúci riadok do:", @@ -4458,7 +4461,7 @@ "Once finished, repeat the check ({check}) to review any remaining issues.": "Po dokončení zopakujte kontrolu ({check}), aby ste skontrolovali všetky zostávajúce problémy.", "Once installed, open the VirtIO ISO and run the installer to complete driver setup.": "Po nainštalovaní otvorte VirtIO ISO a spustite inštalátor na dokončenie ovládačov.", "One or more NVIDIA GPUs are currently configured for VM passthrough (vfio-pci):": "Jedna alebo viac NVIDIA GPU je aktuálne nastavených na VM passthrough (vfio-pci):", - "Online retro games emulator": "Online retro hry emulátor", + "Online retro games emulator": "Online emulátor retro hier", "Only Intel/AMD DRM, Coral and USB nodes can be adopted automatically": "Automaticky je možné prijať iba uzly Intel/AMD DRM, Coral a USB", "Only Intel/AMD DRM, Coral, NPU and USB nodes can be adopted automatically": "Automaticky je možné prijať iba uzly Intel/AMD DRM, Coral, NPU a USB", "Only a Docker Swarm uses these settings, so they are not applied:": "Tieto nastavenia používa iba Docker Swarm, preto sa neuplatnia:", @@ -4476,21 +4479,21 @@ "Only run this if you used LVM (step 6b):": "Spustite iba vtedy, ak ste použili LVM (krok 6b):", "Only the host backup hook is removed — PVE vzdump jobs targeting this storage stay intact.": "Odstráni sa iba hook zálohy hosta - PVE vzdump úlohy smerujúce na toto úložisko zostanú zachované.", "Only the image reference, with no Compose file": "Len odkaz na obraz bez súboru Compose", - "Only the update of the image runs unattended.": "Bezobslužne prebieha iba aktualizácia obrázka.", + "Only the update of the image runs unattended.": "Bezobslužne sa vykonáva iba aktualizácia obrazu.", "Open": "Otvorené", - "Open Source realtime backend in 1 file": "Open Source realtime backend v 1 súbore", + "Open Source realtime backend in 1 file": "Backend s otvoreným zdrojovým kódom a podporou práce v reálnom čase v jedinom súbore", "Open rwx + default inheritance for new files": "Otvorené rwx + predvolené dedenie pre nové súbory", "Open source chat UI for AI models": "Open source chat UI pre modely AI", - "Open source home automation that puts local control and privacy first.": "Open source domácej automatizácie, ktorá dáva miestne ovládanie a súkromie na prvé miesto.", - "Open source, lightweight, native, supports (HTTP, BitTorrent, Magnet, etc.) for downloading.": "Otvorený zdroj, ľahký, natívne, podpora (HTTP, BitTorrent, Magnet, atď) pre stiahnutie.", + "Open source home automation that puts local control and privacy first.": "Domáca automatizácia s otvoreným zdrojovým kódom, ktorá uprednostňuje lokálne ovládanie a súkromie.", + "Open source, lightweight, native, supports (HTTP, BitTorrent, Magnet, etc.) for downloading.": "Nenáročný natívny nástroj na sťahovanie s otvoreným zdrojovým kódom a podporou HTTP, BitTorrentu, magnetických odkazov a ďalších zdrojov.", "Open the VM console and wait for the installer to boot": "Otvorte konzolu VM a počkajte, kým sa spustí inštalátor", "Open the VM console and wait for the loader to boot": "Otvorte konzolu VM a počkajte, kým sa spustí loader", "Open the dashboard from this host on port 8008 to create a new admin account.": "Otvorte dashboard z tohto servera na porte 8008 a vytvorte nový admin účet.", "Open the dashboard to create a new admin account:": "Otvorte dashboard a vytvorte nový admin účet:", - "Open-source AI-powered coding assistant": "Open-source AI-poháňaný asistent kódovania", - "Open-source UI for building and debugging multi-agent and RAG applications": "Open-source UI pre stavebné a ladiace multi-agent a RAG aplikácie", - "Open-source self-hosted SQL IDE.": "Samoobslužné SQL IDE s otvoreným zdrojom.", - "OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw je osobný asistent AI, ktorý beží na svoje vlastné zariadenia", + "Open-source AI-powered coding assistant": "Programátorský asistent využívajúci AI s otvoreným zdrojovým kódom", + "Open-source UI for building and debugging multi-agent and RAG applications": "Rozhranie s otvoreným zdrojovým kódom na tvorbu a ladenie aplikácií s viacerými agentmi a RAG", + "Open-source self-hosted SQL IDE.": "Vývojové prostredie SQL s otvoreným zdrojovým kódom na vlastnom serveri.", + "OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw je osobný asistent AI, ktorý prevádzkujete na vlastných zariadeniach", "OpenList": "OpenList", "OpenShot Video Editor is an award-winning free and open-source video editor for Linux, Mac, and Windows, and is dedicated to delivering high quality video editing and animation solutions to the world.": "OpenShot Video Editor je ocenený bezplatný a open-source video editor pre Linux, Mac a Windows a je venovaný poskytovaniu kvalitných riešení pre editáciu videa a animácie svetu.", "OpenVINO requires a CPU quota to keep the CPU topology": "OpenVINO requires a CPU kvóta pre udržanie topológie procesora", @@ -4499,9 +4502,9 @@ "OpenVSwitch installed successfully": "OpenVSwitch bol úspešne nainštalovaný", "OpenVSwitch is ready to use": "OpenVSwitch je pripravený na použitie", "OpenVSwitch removed": "OpenVSwitch bol odstránený", - "Openssh-server is a sandboxed environment that allows ssh access without giving keys to the entire server.": "Openssh-server je pieskové prostredie, ktoré umožňuje prístup ssh bez toho, aby dal kľúče na celý server.", - "Openvscode-server provides a version of VS Code that runs a server on a remote machine and allows access through a modern web browser.": "Openvscode-server poskytuje verziu VS kódu, ktorý prevádzkuje server na vzdialenom počítači a umožňuje prístup prostredníctvom moderného webového prehliadača.", - "Opera is a multi-platform web browser developed by its namesake company Opera. The browser is based on Chromium, but distinguishes itself from other Chromium-based browsers (Chrome, Edge, etc.) through its user interface and other features.": "Opera je multiplatformový webový prehliadač vyvinutý jeho namesake spoločnosťou Opera. Prehliadač je založený na Chromium, ale odlišuje sa od ostatných prehliadačov založených na Chromium (Chrome, Edge, atď.) prostredníctvom svojho používateľského rozhrania a ďalších funkcií.", + "Openssh-server is a sandboxed environment that allows ssh access without giving keys to the entire server.": "OpenSSH Server poskytuje izolované prostredie s prístupom cez SSH bez sprístupnenia celého servera.", + "Openvscode-server provides a version of VS Code that runs a server on a remote machine and allows access through a modern web browser.": "OpenVSCode Server poskytuje verziu VS Code, ktorá beží na vzdialenom počítači a je prístupná cez moderný webový prehliadač.", + "Opera is a multi-platform web browser developed by its namesake company Opera. The browser is based on Chromium, but distinguishes itself from other Chromium-based browsers (Chrome, Edge, etc.) through its user interface and other features.": "Opera je multiplatformový webový prehliadač vyvíjaný rovnomennou spoločnosťou. Je založený na Chromiu a od ostatných prehliadačov s týmto základom, ako Chrome či Edge, sa odlišuje používateľským rozhraním a ďalšími funkciami.", "Operation": "Operácia", "Operation cancelled by user": "Operácia bola zrušená používateľom", "Operation cancelled by user to create backup.": "Operácia bola zrušená používateľom kvôli vytvoreniu zálohy.", @@ -4511,7 +4514,7 @@ "Operation log": "Záznam operácie", "Operator config re-applied via kernel-agnostic merge": "Vlastné nastavenie správcu bolo znovu použité cez zlúčenie nezávislé od kernelu", "Operator config that WILL be re-applied via kernel-agnostic merge": "Vlastné nastavenie správcu, ktoré SA znovu použije cez zlúčenie nezávislé od kernelu", - "Optical block device (e.g. /dev/sr0)": "Optické blokovacie zariadenie (napr. /dev/sr0)", + "Optical block device (e.g. /dev/sr0)": "Optické blokové zariadenie (napr. /dev/sr0)", "Optimizations detected and ready to revert.": "Optimalizácie boli nájdené a sú pripravené na vrátenie späť.", "Optimize": "Optimalizovať", "Optimize Memory": "Optimalizovať pamäť", @@ -4529,7 +4532,7 @@ "Optional published URL for Jellyfin": "Voliteľné URL pre Jellyfin", "Optional safety helper if you ever need to re-apply manually:": "Voliteľná bezpečnostná pomôcka, ak by ste to niekedy potrebovali použiť ručne znova:", "Optional token from https://www.plex.tv/claim": "Voliteľný token z https://www.plex.tv/claim", - "Optional, not mounted by default": "Nepovinné, štandardne nemontované", + "Optional, not mounted by default": "Voliteľné, predvolene nepripojené", "Optional: Modernize repository sources:": "Voliteľné: modernizovať zdroje repozitárov:", "Optional: apply default ACL so new files inherit permissions:": "Voliteľné: nastaviť predvolené ACL, aby nové súbory zdedili oprávnenia:", "Optional: register this path as Proxmox dir storage:": "Voliteľné: zaregistrovať túto cestu ako Proxmox dir úložisko:", @@ -4540,18 +4543,18 @@ "Or re-run this script and accept the 'apply host permissions' prompt.": "Alebo spustite tento skript znova a potvrďte výzvu na úpravu oprávnení hosta.", "Or use ProxMenux update function": "Alebo použite funkciu aktualizácie v ProxMenux", "Or, if your terminal can't select text, copy it from:": "Alebo ak váš terminál nevie označiť text, skopírujte ho z:", - "Orca Slicer is an open source slicer for FDM printers. OrcaSlicer is fork of Bambu Studio, it was previously known as BambuStudio-SoftFever, Bambu Studio is forked from PrusaSlicer by Prusa Research, which is from Slic3r by Alessandro Ranellucci and the RepRap community": "Orca Slicer je open source krájač pre FDM tlačiarne. OrcaSlicer je fork z Bambu Studio, to bolo predtým známe ako BambuStudio-SoftFever, Bambu Studio je forked z PrusaSlicer od Prusa Research, ktorý je z Slic3r od Alessandro Ranellucci a RepRap komunity", + "Orca Slicer is an open source slicer for FDM printers. OrcaSlicer is fork of Bambu Studio, it was previously known as BambuStudio-SoftFever, Bambu Studio is forked from PrusaSlicer by Prusa Research, which is from Slic3r by Alessandro Ranellucci and the RepRap community": "OrcaSlicer je nástroj s otvoreným zdrojovým kódom na prípravu tlače pre FDM tlačiarne. Vychádza z Bambu Studio a pôvodne sa volal BambuStudio-SoftFever. Bambu Studio vychádza z PrusaSlicer od Prusa Research, ktorého základom je Slic3r od Alessandra Ranellucciho a komunity RepRap.", "Original ZFS ARC config restored from .bak": "Pôvodné nastavenie ZFS ARC bolo obnovené zo súboru .bak", "Original bashrc restored": "Pôvodný bashrc bol obnovený", "Original logrotate configuration restored": "Pôvodné nastavenie logrotate bolo obnovené", "Orphan stack contract archived:": "@ info: whatsthis", - "Oscam is an Open Source Conditional Access Module software used for descrambling DVB transmissions using smart cards. It's both a server and a client.": "Oscam je softvér Open Source Conditional Access Module používaný pre descrambling DVB transmissions pomocou inteligentných kariet. Je to server aj klient.", + "Oscam is an Open Source Conditional Access Module software used for descrambling DVB transmissions using smart cards. It's both a server and a client.": "OSCam je modul podmieneného prístupu s otvoreným zdrojovým kódom na dekódovanie vysielania DVB pomocou čipových kariet. Funguje ako server aj klient.", "Other Prebuilt Linux VMs": "Ďalšie pripravené Linux VM", "Output archive:": "Výstupný archív:", "Overseerr is a request management and media discovery tool built to work with your existing Plex ecosystem.": "Overseerr je nástroj na riadenie žiadostí a vyhľadávanie médií postavený na prácu s existujúcim ekosystémom Plex.", "Owner:": "Vlastník:", "Ownership set to root:sharedfiles with 2775 on:": "Vlastník nastavený na root:sharedfiles s oprávnením 2775 na:", - "P2P bittorrent download": "P2P bittorrent stiahnuť", + "P2P bittorrent download": "Sťahovanie cez P2P sieť BitTorrent", "PAM limits configured": "Limity PAM sú nastavené", "PBS API log rotation configured (hourly, size-based)": "Rotácia logov PBS API je nastavená (každú hodinu, podľa veľkosti)", "PBS backup error log": "Záznam chyby zálohy do PBS", @@ -4598,7 +4601,7 @@ "Packaging OVA file...": "Balím OVA súbor...", "Packing installer archive...": "Balím archív inštalátora...", "PairDrop is a sublime alternative to AirDrop that works on all platforms. Send images, documents or text via peer to peer connection to devices in the same local network/Wi-Fi or to paired devices.": "PairDrop je skvelá alternatíva k AirDrop, ktorá pracuje na všetkých platformách. Poslať obrázky, dokumenty alebo text prostredníctvom peer-to-peer pripojenie k zariadeniam v rovnakej miestnej sieti/Wi-Fi alebo spárované zariadenia.", - "Paperless configuration cancelled": "Bezpapierová konfigurácia zrušená", + "Paperless configuration cancelled": "Nastavenie Paperless bolo zrušené", "Paperless-ngx WebUI": "Paperless-ngx WebUI", "Parsing OVF descriptor...": "Čítam OVF popisovač...", "Partial VM removed": "Čiastočná VM odstránená", @@ -4606,14 +4609,14 @@ "Partition created": "Oddiel bol vytvorený", "Partition created:": "Oddiel vytvorený:", "Partition table wiped": "Tabuľka oddielov bola vymazaná", - "Pass /dev/kvm to the LXC": "prejsť /dev/kvm na LXC", - "Pass /dev/net/tun to the LXC": "priepust /dev/net/tun na LXC", - "Pass /dev/ttyUSB0 to the LXC": "prejsť /dev/ttyUSB0 na LXC", - "Pass /dev/video10 to the LXC": "Prešiel /dev/video10 na LXC", - "Pass /dev/video11 to the LXC": "Prešiel /dev/video11 na LXC", - "Pass /dev/video12 to the LXC": "Prechod /dev/video12 na LXC", - "Pass a host device to the LXC": "Posuňte hostiteľské zariadenie na LXC", - "Pass the UPS to the LXC": "Pošlite UPS do LXC", + "Pass /dev/kvm to the LXC": "Sprístupniť /dev/kvm kontajneru LXC", + "Pass /dev/net/tun to the LXC": "Sprístupniť /dev/net/tun kontajneru LXC", + "Pass /dev/ttyUSB0 to the LXC": "Sprístupniť /dev/ttyUSB0 kontajneru LXC", + "Pass /dev/video10 to the LXC": "Sprístupniť /dev/video10 kontajneru LXC", + "Pass /dev/video11 to the LXC": "Sprístupniť /dev/video11 kontajneru LXC", + "Pass /dev/video12 to the LXC": "Sprístupniť /dev/video12 kontajneru LXC", + "Pass a host device to the LXC": "Sprístupniť zariadenie hostiteľa kontajneru LXC", + "Pass the UPS to the LXC": "Sprístupniť UPS kontajneru LXC", "Passing NVIDIA does not enable acceleration inside the application. The host needs NVIDIA Container Toolkit and a compatible image. Continue?": "Prejdenie NVIDIA neumožní zrýchlenie v aplikácii. Hostiteľ potrebuje NVIDIA Container Toolkit a kompatibilný obrázok. Pokračovať?", "Passphrase used to unlock the imported keyfile (leave blank if the keyfile is unencrypted / kdf=none):": "Fráza na odomknutie importovaného keyfile (nechajte prázdne, ak keyfile nie je šifrovaný / kdf=none):", "Passphrases do not match.": "Frázy sa nezhodujú.", @@ -4640,7 +4643,7 @@ "Password of the LibreDB Studio administrator": "Heslo správcu LibreDB Studio", "Password of the MariaDB user": "Heslo používateľa MariaDB", "Password of the MineOS web interface user": "Heslo používateľa webového rozhrania MineOS", - "Password of the NUT server user": "Heslo používateľa NUT server", + "Password of the NUT server user": "Heslo používateľa servera NUT", "Password of the NetBox admin account": "Heslo admin účtu NetBox", "Password of the OpenList admin user": "Heslo užívateľa OpenList admin", "Password of the PhotoPrism admin user (at least 8 characters)": "Heslo PhotoPrism admin užívateľa (aspoň 8 znakov)", @@ -4648,7 +4651,7 @@ "Password of the ShareCoveX panel administrator (8 characters or more)": "Heslo správcu panela ShareCoveX (8 znakov alebo viac)", "Password of the SnapOtter admin user": "Heslo užívateľa SnapOtter admin", "Password of the admin account": "Heslo účtu správcu", - "Password of the deprecated Flowise application login (only read by Flowise versions before 3.0.1)": "Heslo depreciovaného prihlasovacieho protokolu aplikácie Flowise (čítané iba verziami Flowise pred 3.0.1)", + "Password of the deprecated Flowise application login (only read by Flowise versions before 3.0.1)": "Heslo zastaraného prihlasovania aplikácie Flowise (používajú iba verzie pred 3.0.1)", "Password of the main AdGuard Home": "Heslo hlavného AdGuard Home", "Password of the superuser account": "Heslo účtu superužívateľa", "Password or API token secret:": "Heslo alebo tajná časť API tokenu:", @@ -4667,7 +4670,7 @@ "Path does not exist.": "Cesta neexistuje.", "Path inside the container": "Cesta vnútri kontajnera", "Path inside the container (e.g. /media-extra)": "Cesta vnútri kontajnera (napr. /media-extra)", - "Path inside the remote (empty = root)": "Cesta vnútri diaľkového (prázdne = koreň)", + "Path inside the remote (empty = root)": "Cesta vo vzdialenom úložisku (prázdne = koreň)", "Path must be absolute (start with /)": "Cesta musí byť absolútna (začínať znakom /)", "Path must be absolute (start with /).": "Cesta musí byť absolútna (začínať znakom /).", "Path not found": "Cesta sa nenašla", @@ -4681,9 +4684,9 @@ "Paths skipped:": "Preskočené cesty:", "Paths to back up:": "Cesty na zálohovanie:", "Paths:": "Cesty:", - "Peers reach the server through the public address and the UDP port given during the installation, so that port must be forwarded to this container.": "Peers dosiahnuť server prostredníctvom verejnej adresy a UDP port daný počas inštalácie, tak, aby port musí byť odoslaný do tohto kontajnera.", - "Peers to create: a number (3) or a list of names (phone,laptop)": "Peers vytvoriť: číslo (3) alebo zoznam mien (telefón,laptop)", - "Pelorus is an AI navigator for Selkies-powered Linux desktops. Pelorus runs a FastAPI server that gives an LLM agent (Ollama, OpenAI-compatible, or Gemini) control over mouse, keyboard, screenshot, and window management via the Pixelflux computer-use backend, a Linux accessibility tree (AT-SPI), and optional KWin D-Bus integration.": "Pelorus je navigátor UI pre Linuxové plochy poháňané Selkiesom. Pelorus spustí FastAPI server, ktorý dáva LLM agenta (Ollama, OpenAI-kompatibilný, alebo Gemini) kontrolu nad myšou, klávesnice, screenshot, a správu okien cez Pixelflux počítač-použitie backend, Linux prístupnosť strom (AT-SPI), a voliteľné KWin D-Bus integrácie.", + "Peers reach the server through the public address and the UDP port given during the installation, so that port must be forwarded to this container.": "Účastníci sa pripájajú k serveru cez verejnú adresu a port UDP zadané počas inštalácie. Tento port musí byť presmerovaný do kontajnera.", + "Peers to create: a number (3) or a list of names (phone,laptop)": "Účastníci na vytvorenie: počet (3) alebo zoznam mien (phone,laptop)", + "Pelorus is an AI navigator for Selkies-powered Linux desktops. Pelorus runs a FastAPI server that gives an LLM agent (Ollama, OpenAI-compatible, or Gemini) control over mouse, keyboard, screenshot, and window management via the Pixelflux computer-use backend, a Linux accessibility tree (AT-SPI), and optional KWin D-Bus integration.": "Pelorus je navigátor AI pre linuxové pracovné plochy využívajúce Selkies. Server FastAPI dáva agentovi LLM (Ollama, model kompatibilný s OpenAI alebo Gemini) ovládanie myši, klávesnice, snímok obrazovky a správy okien cez backend Pixelflux, strom prístupnosti Linuxu AT-SPI a voliteľnú integráciu KWin D-Bus.", "Pending components:": "Trvalé komponenty:", "Pending restore ID:": "ID čakajúcej obnovy:", "Pending restore dir:": "Priečinok čakajúcej obnovy:", @@ -4714,9 +4717,9 @@ "Persistent disk reused:": "Trvalý disk opätovne použitý:", "Persistent:": "Trvalé:", "Personal finance management application": "Aplikácia na správu osobných financií", - "Photo and video library with optional GPU transcoding and machine learning": "Foto a video knižnica s voliteľným GPU transkódovanie a strojové učenie", + "Photo and video library with optional GPU transcoding and machine learning": "Knižnica fotografií a videí s voliteľným prevodom videa pomocou GPU a strojovým učením", "PhotoPrism": "PhotoPrism", - "Phpmyadmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. phpMyAdmin supports a wide range of operations on MySQL and MariaDB.": "Phpmyadmin je bezplatný softvérový nástroj napísaný v PHP, určený na zvládnutie správy MySQL cez web. phpMyAdmin podporuje širokú škálu operations na MySQL a MariaDB.", + "Phpmyadmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. phpMyAdmin supports a wide range of operations on MySQL and MariaDB.": "phpMyAdmin je bezplatný nástroj napísaný v PHP na správu MySQL cez web. Podporuje širokú škálu operácií nad MySQL a MariaDB.", "Physical Function with": "Fyzická funkcia s", "Physical interface": "Fyzické rozhranie", "Physical interfaces available": "Dostupné fyzické rozhrania", @@ -4730,8 +4733,8 @@ "Pick an SSH private key (auto-detected on this host):": "Vyberte súkromný SSH kľúč (automaticky nájdený na tomto hostovi):", "Pick an alternative way to authorize the new key:": "Vyberte iný spôsob autorizácie nového kľúča:", "Pidgin is a chat program which lets you log into accounts on multiple chat networks simultaneously. This means that you can be chatting with friends on XMPP and sitting in an IRC channel at the same time.": "Pidgin je program chatu, ktorý vám umožňuje prihlásiť sa do účtov na viacerých chatových sieťach súčasne. To znamená, že môžete chatovať s priateľmi na XMPP a zároveň sedieť v IRC kanáli.", - "Piper is a fast, local neural text to speech system that sounds great and is optimized for the Raspberry Pi 4. This container provides a Wyoming protocol server for Piper.": "Piper je rýchly, miestny nervový text do rečového systému, ktorý znie skvele a je optimalizovaný pre Malina Pi 4. Tento kontajner poskytuje Wyoming protokol server pre Piper.", - "Piwigo is a photo gallery software for the web that comes with powerful features to publish and manage your collection of pictures.": "Piwigo je softvér fotogalérie pre web, ktorý prichádza s výkonnými funkciami publikovať a spravovať vašu zbierku obrázkov.", + "Piper is a fast, local neural text to speech system that sounds great and is optimized for the Raspberry Pi 4. This container provides a Wyoming protocol server for Piper.": "Piper je rýchly lokálny systém prevodu textu na reč pomocou neurónových sietí, optimalizovaný pre Raspberry Pi 4. Tento kontajner poskytuje server protokolu Wyoming pre Piper.", + "Piwigo is a photo gallery software for the web that comes with powerful features to publish and manage your collection of pictures.": "Piwigo je webová fotogaléria s bohatými funkciami na publikovanie a správu zbierky obrázkov.", "Placeholders: {{guestname}} {{node}} {{vmid}}": "Zástupné symboly: {{guestname}} {{node}} {{vmid}}", "Planka is an elegant open source project tracking tool.": "Planka je elegantný open source nástroj na sledovanie projektov.", "Please check network connectivity.": "Skontrolujte sieťové pripojenie.", @@ -4752,7 +4755,7 @@ "Please select a valid option": "Vyberte platnú možnosť", "Please use an SSH session (Linux, macOS, Windows/PuTTY) or a physical console to perform the upgrade.": "Na aktualizáciu použite SSH reláciu (Linux, macOS, Windows/PuTTY) alebo fyzickú konzolu.", "Plex WebUI": "Plex WebUI", - "Plex organizes video, music and photos from personal media libraries and streams them to smart TVs, streaming boxes and mobile devices. This container is packaged as a standalone Plex Media Server. Straightforward design and bulk actions mean getting things done faster.": "Plex organizuje video, hudbu a fotografie z osobných mediálnych knižníc a vysiela ich do smart TV, streaming boxov a mobilných zariadení. Tento kontajner je balený ako samostatný Plex Media Server. Straightforward dizajn a hromadný akcie znamená dostať veci robiť rýchlejšie.", + "Plex organizes video, music and photos from personal media libraries and streams them to smart TVs, streaming boxes and mobile devices. This container is packaged as a standalone Plex Media Server. Straightforward design and bulk actions mean getting things done faster.": "Plex usporadúva videá, hudbu a fotografie z osobných knižníc a streamuje ich do inteligentných televízorov, prehrávačov a mobilných zariadení. Tento kontajner obsahuje samostatný Plex Media Server. Jednoduché ovládanie a hromadné akcie urýchľujú prácu.", "PocketBase admin UI": "používateľské rozhranie správcu PocketBase", "Podcast synchronization service": "Služba synchronizácie podcastov", "Pool does not appear to use SSD/NVMe devices with discard support. Skipping ZFS autotrim for pool:": "Pool pravdepodobne nepoužíva SSD/NVMe zariadenia s podporou discard. ZFS autotrim sa preskočí pre pool:", @@ -4773,13 +4776,13 @@ "Postfix configuration": "Nastavenie Postfixu", "PostgreSQL": "PostgreSQL", "PostgreSQL URL without an associated service:": "PostgreSQL URL bez súvisiacej služby:", - "PostgreSQL creates the database named in POSTGRES_DB on the first start. The installer default is postgresql.": "PostgreSQL vytvára databázu pomenovanú v POSTGRES_DB na prvom začiatku. Štandardný inštalátor je postgresql.", - "PostgreSQL is an advanced, enterprise-class, and open-source relational database system. PostgreSQL supports both SQL (relational) and JSON (non-relational) querying.": "PostgreSQL je moderný, podnikový a open-source relačný databázový systém. PostgreSQL podporuje ako SQL (relatívne) tak JSON (nerelatívne) dotazovanie.", - "PostgreSQL requires a password": "PostgreSQL requires heslo", + "PostgreSQL creates the database named in POSTGRES_DB on the first start. The installer default is postgresql.": "PostgreSQL pri prvom spustení vytvorí databázu s názvom v POSTGRES_DB. Inštalačný sprievodca predvolene používa názov postgresql.", + "PostgreSQL is an advanced, enterprise-class, and open-source relational database system. PostgreSQL supports both SQL (relational) and JSON (non-relational) querying.": "PostgreSQL je pokročilý relačný databázový systém podnikovej úrovne s otvoreným zdrojovým kódom. Podporuje relačné dotazy SQL aj nerelačné dotazy nad JSON.", + "PostgreSQL requires a password": "PostgreSQL vyžaduje heslo", "PostgreSQL volume size in GB": "Veľkosť dátového zväzku PostgreSQL v GB", "Potential QEMU startup/assertion failures": "možné zlyhania štartu alebo assertion chyby QEMU", "Power state D3cold/D0 transitions may be inaccessible": "prechody napájacích stavov D3cold/D0 nemusia byť dostupné", - "Powerful OCR powered by DeepSeek AI": "Výkonný OCR poháňaný DeepSeek AI", + "Powerful OCR powered by DeepSeek AI": "Výkonné rozpoznávanie textu OCR pomocou DeepSeek AI", "Powerful networking tool": "Silný sieťový nástroj", "Pre-check found": "Predbežná kontrola našla", "Pre-configure destinations so you don't have to enter them every time you back up.": "Nastavte si ciele vopred, aby ste ich nemuseli zadávať pri každej zálohe.", @@ -4832,8 +4835,8 @@ "Previous installation restored": "Predchádzajúca inštalácia obnovená", "Previous shutdowns": "Predchádzajúce vypnutia", "Primary GID for Emby": "Primárne GID pre Emby", - "Privacy-first finance app with envelope budgeting and multi-device sync.": "Privacy-first finance app s obálkou budgeting a multi-device synchronizácia.", - "Privacy-first, self-hosted PDF toolkit": "Súprava nástrojov PDF s vlastným hosťovaním na prvom mieste", + "Privacy-first finance app with envelope budgeting and multi-device sync.": "Finančná aplikácia s dôrazom na súkromie, obálkovým rozpočtom a synchronizáciou viacerých zariadení.", + "Privacy-first, self-hosted PDF toolkit": "Súprava nástrojov PDF na vlastnom serveri s dôrazom na súkromie", "Private installation record saved": "Uložené súkromné záznamy o inštalácii", "Private network assigned automatically:": "Privátne siete pridelené automaticky:", "Private network kept, because other containers still use it:": "Súkromná sieť ponechaná, pretože ju stále používajú iné kontajnery:", @@ -4854,7 +4857,7 @@ "Privileged container — host root maps directly, no permission changes needed": "Privilegovaný kontajner - root hosta sa mapuje priamo, nie sú potrebné zmeny oprávnení", "Privileged containers can access host devices directly": "Privilegované kontajnery môžu priamo pristupovať k zariadeniam servera", "Privileged containers have full root access to the host system!": "Privilegované kontajnery majú plný root prístup k hostiteľskému systému!", - "Privileged installation declined": "Privilegovaná inštalácia odmietla", + "Privileged installation declined": "Privilegovaná inštalácia bola odmietnutá", "Privileged: Full host access (less secure)": "Privilegovaný: plný prístup k serveru (menej bezpečné)", "Probe failed": "Sonda zlyhala", "Proceed": "Pokračovať", @@ -4872,7 +4875,7 @@ "Proposed ZFS ARC maximum:": "Navrhované maximum ZFS ARC:", "Provided by newer version — skipping": "Poskytuje novšia verzia - preskakujem", "Prowlarr does not offer the application schema:": "Prowlarr neponúka schému aplikácie:", - "Prowlarr is a indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Sonarr, Radarr, Lidarr, and Readarr offering complete management of your indexers with no per app Indexer setup required (we do it all).": "Prowlarr je indexátor manažér/proxy postavený na populárnej arr .net / Reactjs base stack integrovať s rôznymi PVR aplikácie. Prowlarr podporuje ako Torrent Trackers a Usenet Indexers. To integruje bezproblémovo s Sonarr, Radarr, Lidarr, a Readarr ponúka kompletné riadenie vašich indexerov bez na app Indexer nastavenie required (robíme to všetko).", + "Prowlarr is a indexer manager/proxy built on the popular arr .net/reactjs base stack to integrate with your various PVR apps. Prowlarr supports both Torrent Trackers and Usenet Indexers. It integrates seamlessly with Sonarr, Radarr, Lidarr, and Readarr offering complete management of your indexers with no per app Indexer setup required (we do it all).": "Prowlarr je správca indexerov a proxy na základe technológií .NET a React používaných aplikáciami *arr. Podporuje torrentové trackery aj indexery Usenetu. Prepája sa so Sonarr, Radarr, Lidarr a Readarr a spravuje ich indexery bez samostatného nastavovania v každej aplikácii.", "ProxMenux Information": "Informácie o ProxMenux", "ProxMenux Monitor": "ProxMenux Monitor", "ProxMenux Monitor Service Verification": "Kontrola služby ProxMenux Monitor", @@ -4946,11 +4949,11 @@ "Proxmox web interface: Datacenter > Storage > Add > ZFS": "Webové rozhranie Proxmoxu: Datacenter > Storage > Add > ZFS", "Proxmox web interface: Datacenter > Storage > Add > iSCSI": "Webové rozhranie Proxmoxu: Datacenter > Storage > Add > iSCSI", "Proxy address to reach the API (optional)": "Adresa proxy na prístup k API (voliteľné)", - "Public UDP port clients connect to": "Verejné UDP port klienti pripojiť k", - "Public UDP port peers connect to": "Verejné UDP portové rovesníci pripojiť k", + "Public UDP port clients connect to": "Verejný port UDP na pripojenie klientov", + "Public UDP port peers connect to": "Verejný port UDP na pripojenie účastníkov", "Public URL of phpMyAdmin when it is served behind a reverse proxy": "Verejné URL phpMyAdmin, keď sa podáva za reverzným proxy", - "Public address clients connect to (vpn.example.com or a public IP)": "Klienti verejnej adresy sa pripájajú k (vpn.example.com alebo verejné IP)", - "Public address peers connect to, or auto to detect it (vpn.example.com)": "Verejná adresa peers pripojiť, alebo auto na detekciu (vpn.example.com)", + "Public address clients connect to (vpn.example.com or a public IP)": "Verejná adresa na pripojenie klientov (vpn.example.com alebo verejná IP adresa)", + "Public address peers connect to, or auto to detect it (vpn.example.com)": "Verejná adresa, ku ktorej sa pripájajú účastníci, alebo auto na jej zistenie (vpn.example.com)", "Publish each directory from Resources: open it, switch on SMB, NFS or both, and save.": "Zverejnite každý adresár zo zdrojov: otvorte ho, zapnite SMB, NFS alebo oboje a uložte.", "Pulling latest changes from GitHub...": "Sťahujem najnovšie zmeny z GitHubu...", "Purge the gasket-dkms package": "Úplne odstrániť balík gasket-dkms", @@ -4958,11 +4961,11 @@ "Purging log2ram apt package...": "Úplne odstraňujem balík log2ram cez APT...", "Put this application under watchdog? It is restarted automatically when it crashes.": "Dať túto aplikáciu pod watchdog? Pri zlyhaní sa automaticky reštartuje.", "Put this application under watchdog? It is restarted automatically when it crashes. A stop or a shutdown you ask for is never undone.": "Dať túto aplikáciu pod watchdog? Pri zlyhaní sa automaticky reštartuje. Zastavenie alebo vypnutie, o ktoré požiadate, sa nikdy nevráti späť.", - "Pwndrop is a self-deployable file hosting service for sending out red teaming payloads or securely sharing your private files over HTTP and WebDAV.": "Pwndrop je samozamestnávateľná serverová hostingová služba pre odosielanie červeného spojovacieho užitočného zaťaženia alebo bezpečné zdieľanie vašich súkromných súborov cez HTTP a WebDAV.", - "PyCharm offers out-of-the-box support for Python, databases, Jupyter, Git, Conda, PyTorch, TensorFlow, Hugging Face, Django, Flask, FastAPI, and more.": "PyCharm ponúka mimo-of-the-box podporu pre Python, databázy, Jupyter, Git, Conda, PyTorch, TensorFlow, Hugging Face, Django, Flask, FastAPI a ďalšie.", - "Pydio Cells needs an external MySQL or MariaDB database. The setup wizard asks for its address, database name and user on the first start.": "Pydio Cells potrebuje externú MySQL alebo MariaDB databázu. Sprievodca nastavením žiada o svoju adresu, meno databázy a užívateľa pri prvom štarte.", - "Pydio Cells redirects to the address given in EXTERNALURL. If the container changes address, edit lxc.environment.runtime: EXTERNALURL and SERVER_IP in /etc/pve/lxc/.conf with the container stopped, and delete /config/keys/cert.crt to regenerate the certificate.": "Pydio Cells presmeruje na adresu uvedenú v EXTERNURL. Ak kontajner zmení adresu, upravte lxc.environment.runtime: EXTERNURL a SERVER_IP v /etc/pve/lxc/.conf s kontajnerom zastavil, a odstráňte /config/keys/cert.crt pre regeneráciu certifikátu.", - "Pydio-cells is the nextgen file sharing platform for organizations. It is a full rewrite of the Pydio project using the Go language following a micro-service architecture.": "Pydio-bunky je platforma pre zdieľanie súborov pre organizácie. Je to úplný prepis projektu Pydio pomocou jazyka Go podľa architektúry mikroslužieb.", + "Pwndrop is a self-deployable file hosting service for sending out red teaming payloads or securely sharing your private files over HTTP and WebDAV.": "Pwndrop je služba na vlastnom serveri na zdieľanie súborov cez HTTP a WebDAV, určená na doručovanie testovacích súborov pri simulácii útokov aj bezpečné zdieľanie súkromných súborov.", + "PyCharm offers out-of-the-box support for Python, databases, Jupyter, Git, Conda, PyTorch, TensorFlow, Hugging Face, Django, Flask, FastAPI, and more.": "PyCharm predvolene podporuje Python, databázy, Jupyter, Git, Conda, PyTorch, TensorFlow, Hugging Face, Django, Flask, FastAPI a ďalšie technológie.", + "Pydio Cells needs an external MySQL or MariaDB database. The setup wizard asks for its address, database name and user on the first start.": "Pydio Cells potrebuje externú databázu MySQL alebo MariaDB. Pri prvom spustení sa sprievodca opýta na adresu servera, názov databázy a používateľa.", + "Pydio Cells redirects to the address given in EXTERNALURL. If the container changes address, edit lxc.environment.runtime: EXTERNALURL and SERVER_IP in /etc/pve/lxc/.conf with the container stopped, and delete /config/keys/cert.crt to regenerate the certificate.": "Pydio Cells presmerúva na adresu v EXTERNALURL. Ak sa adresa kontajnera zmení, zastavte ho, upravte lxc.environment.runtime: EXTERNALURL a SERVER_IP v /etc/pve/lxc/.conf a odstráňte /config/keys/cert.crt, aby sa certifikát vytvoril znova.", + "Pydio-cells is the nextgen file sharing platform for organizations. It is a full rewrite of the Pydio project using the Go language following a micro-service architecture.": "Pydio Cells je platforma na zdieľanie súborov pre organizácie. Ide o úplný prepis projektu Pydio v jazyku Go s architektúrou mikroslužieb.", "QDirStat Qt-based directory statistics: KDirStat without any KDE -- from the author of the original KDirStat.": "QDirStat Štatistika adresára založená na Qt: KDirStat bez KDE -- od autora pôvodného KDirStat.", "Quick health check (PASSED / FAILED)": "Rýchla kontrola stavu (PASSED / FAILED)", "Quick health status — overall SMART result + key attributes": "Rýchly stav zdravia - celkový SMART výsledok + hlavné údaje", @@ -4981,27 +4984,27 @@ "RPC Bind Service: RUNNING": "Služba RPC Bind: BEŽÍ", "RPC Bind Service: STOPPED": "Služba RPC Bind: ZASTAVENÁ", "RPC Bind Service: STOPPED - starting...": "Služba RPC Bind: ZASTAVENÁ - spúšťam...", - "RPCS3 is a multi-platform open-source Sony PlayStation 3 emulator and debugger written in C++ for Windows, Linux, macOS and FreeBSD.": "RPCS3 je multi-platforma open-source Sony PlayStation 3 emulátor a debugger napísaný v C++ pre Windows, Linux, macOS a FreeBSD.", - "Radarr - A fork of Sonarr to work with movies à la Couchpotato.": "Radarr - A fork Sonarr pracovať s filmami à la Couchpotato.", + "RPCS3 is a multi-platform open-source Sony PlayStation 3 emulator and debugger written in C++ for Windows, Linux, macOS and FreeBSD.": "RPCS3 je multiplatformový emulátor a ladiaci nástroj Sony PlayStation 3 s otvoreným zdrojovým kódom, napísaný v C++ pre Windows, Linux, macOS a FreeBSD.", + "Radarr - A fork of Sonarr to work with movies à la Couchpotato.": "Radarr – odnož Sonarr na správu filmov podobne ako CouchPotato.", "Radarr added to Prowlarr": "Radarr pridaný do Prowlarr", "Radarr connected to qBittorrent": "Radarr napojený na qBittorrent", "Radarr root folder configured": "Konfigurovaný koreňový priečinok Radarr", "RagFlow is an open-source RAG engine based on deep document understanding.": "RagFlow je open-source motor RAG založený na hlbokom porozumení dokumentov.", - "Raneto - is an open source Knowledgebase platform that uses static Markdown files to power your Knowledgebase.": "Raneto - je open source Knowledgebase platforma, ktorá používa statické Markdown súbory pre napájanie znalostnej základne.", + "Raneto - is an open source Knowledgebase platform that uses static Markdown files to power your Knowledgebase.": "Raneto je platforma znalostnej databázy s otvoreným zdrojovým kódom, ktorá používa statické súbory Markdown.", "Raneto web interface": "Webové rozhranie Raneto", - "RawTherapee is a free, cross-platform raw image processing program!": "RawTherapee je zadarmo, cross-platform surový program spracovania obrazu!", + "RawTherapee is a free, cross-platform raw image processing program!": "RawTherapee je bezplatný multiplatformový program na spracovanie fotografií vo formáte RAW.", "Rclone WebUI": "Rclone WebUI", - "Rclone mount": "Prípojka Rclón", + "Rclone mount": "Pripojenie Rclone", "Rclone mount active": "Comment", - "Rclone mount needs a privileged LXC with FUSE access. The container is dedicated to Rclone and its web UI must not be exposed to untrusted networks.": "Rclone mount potrebuje privilegovaný LXC s FUSE prístupom. Kontajner je určený Rclone a jeho webové UI nesmie byť vystavený nedôveryhodným sieťam.", - "Rclone mount requires a privileged LXC with FUSE access. Use this profile only on a trusted node and network.": "Rclon mount requires privilegovaný LXC s FUSE prístupom. Použite tento profil len na dôveryhodnom uzle a sieti.", + "Rclone mount needs a privileged LXC with FUSE access. The container is dedicated to Rclone and its web UI must not be exposed to untrusted networks.": "Pripojenie Rclone vyžaduje privilegovaný kontajner LXC s prístupom k FUSE. Kontajner je vyhradený pre Rclone a jeho webové rozhranie nesmie byť prístupné z nedôveryhodných sietí.", + "Rclone mount requires a privileged LXC with FUSE access. Use this profile only on a trusted node and network.": "Pripojenie cez Rclone vyžaduje privilegovaný kontajner LXC s prístupom k FUSE. Tento profil používajte iba na dôveryhodnom uzle a v dôveryhodnej sieti.", "Rclone mount requires a privileged container": "Rclon mount requires privilegovaný kontajner", "Re-enter the BORG REPOKEY passphrase to confirm:": "Zadajte BORG REPOKEY frázu znova na potvrdenie:", "Re-running pre-check after repairs...": "Spúšťam predbežnú kontrolu znova po opravách...", "Reachable": "Dostupné", "Read an oci-compose from a file of this host": "Načítať oci-compose zo súboru tohto hostiteľa", "Read its Compose file from a file of this host": "Načítať súbor Compose z tohto hostiteľa", - "Read the link with pct console CTID on the Proxmox host, or from the Console panel of the container in the Proxmox web interface, then open the https://playit.gg/claim/ address it shows in a browser and sign in to playit.gg. Ctrl+a q leaves pct console.": "Prečítajte si odkaz s pct konzolou CTID na Proxmox hostiteľa, alebo z panelu konzoly kontajnera vo webovom rozhraní Proxmox, potom otvorte https://playit.gg/claim/ adresu, ktorú zobrazí v prehliadači a prihláste sa na playit.gg. Ctrl+a q opustí pct konzolu.", + "Read the link with pct console CTID on the Proxmox host, or from the Console panel of the container in the Proxmox web interface, then open the https://playit.gg/claim/ address it shows in a browser and sign in to playit.gg. Ctrl+a q leaves pct console.": "Odkaz zobrazíte na hostiteľovi Proxmox príkazom pct console CTID alebo v paneli Console kontajnera vo webovom rozhraní Proxmox. Zobrazenú adresu https://playit.gg/claim/ otvorte v prehliadači a prihláste sa do playit.gg. Konzolu pct console ukončíte pomocou Ctrl+a q.", "Read-Only": "Iba na čítanie", "Read-Only access": "Prístup iba na čítanie", "Read-Write (universal)": "Čítanie/zápis (univerzálne)", @@ -5010,7 +5013,7 @@ "Read-only access (or no write permissions).": "Prístup iba na čítanie (alebo chýba oprávnenie na zápis).", "Read-only mount": "Pripojiť iba na čítanie", "Read/Write (default)": "Čítanie/zápis (predvolené)", - "Read/write": "Čítať/písať", + "Read/write": "Čítanie a zápis", "Read/write CPU model-specific registers": "Čítať/zapisovať modelovo špecifické registre CPU", "Readable user table (UID, shell, etc.)": "Prehľadná tabuľka používateľov (UID, shell a pod.)", "Reading NVMe SMART data...": "Čítam NVMe SMART dáta...", @@ -5021,7 +5024,7 @@ "Reading image metadata. Please wait...": "Načítavajú sa metadáta obrazu. Čakajte, prosím...", "Real-time Performance Monitoring": "Monitorovanie výkonnosti v reálnom čase", "Real-time bandwidth usage (press q to exit)": "Využitie priepustnosti v reálnom čase (ukončíte klávesom q)", - "Real-time collaborative document editor": "Kolektívne editor dokumentov v reálnom čase", + "Real-time collaborative document editor": "Editor dokumentov so spoluprácou v reálnom čase", "Real-time network monitoring (press q to exit)": "Sieťový monitoring v reálnom čase (ukončíte klávesom q)", "Real-time network usage (iftop)": "Sieťová prevádzka v reálnom čase (iftop)", "Reason: Access denied": "Dôvod: Prístup zamietnutý", @@ -5038,7 +5041,7 @@ "Rebooting the system...": "Reštartujem systém...", "Rebuilding initramfs after vfio.conf cleanup...": "Prebudovanie initramfs po vyčistení vfio.conf...", "Rebuilding initramfs to apply nouveau blacklist before installation...": "Znovu vytváram initramfs, aby sa blacklist nouveau použil ešte pred inštaláciou...", - "Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "Prebudovanie kontajnera a jeho obnovenie zo zálohy sa testuje na rovnakom obrázku; prvá aktualizácia na novší obrázok je ešte potrebné pozorovať.", + "Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "Opätovné vytvorenie kontajnera aj obnovenie zo zálohy sú otestované s rovnakým obrazom. Prvú aktualizáciu na novší obraz ešte treba overiť.", "Recent": "Nedávne", "Recent PBS tasks": "Nedávne úlohy PBS", "Recent Samba Servers": "Posledné Samba servery", @@ -5125,7 +5128,7 @@ "Reinstalled Proxmox packages successfully": "Balíky Proxmoxu boli úspešne preinštalované", "Reinstalling": "Preinštalovávam", "Reinstalling core Proxmox packages...": "Preinštalovávam základné balíky Proxmoxu...", - "Relative CPU priority (cpuunits)": "Relatívna priorita procesora (cpu jednotky)", + "Relative CPU priority (cpuunits)": "Relatívna priorita CPU (cpuunits)", "Release Channel": "Vetva vydania", "Release channel set to Beta.": "Vetva vydania je nastavená na Beta.", "Release channel set to Stable.": "Vetva vydania je nastavená na stabilnú verziu.", @@ -5277,7 +5280,7 @@ "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Zdroje úložiska sa zmenili, ale obnovenie zoznamu balíkov APT zlyhalo. Prevádzka zastavená; zdroje neboli vrátené späť. Pred pokračovaním skontrolujte Node > Updates > Repository a zopakujte apt-get update.", "Repository switch declined; no APT source changed.": "Prepnutie úložiska bolo odmietnuté; žiadny zdroj APT sa nezmenil.", "Repository:": "Repozitár:", - "Request a staging certificate for testing: true or false": "Vyžiada si osvedčenie o intenzite na testovanie: pravdivé alebo nepravdivé", + "Request a staging certificate for testing: true or false": "Požiadať o testovací certifikát staging: true alebo false", "Require reboot": "Vyžaduje reštart", "Required command not found:": "Požadovaný príkaz sa nenašiel:", "Required if using a VirtIO or SCSI disk.": "Vyžaduje sa pri použití VirtIO alebo SCSI disku.", @@ -5295,13 +5298,13 @@ "Reset current storage selection": "Vynulovať aktuálny výber úložiska", "Resetting time synchronization...": "Resetujem synchronizáciu času...", "Residual Bookworm entries commented where applicable": "Zostávajúce položky Bookworm boli zakomentované, kde to bolo potrebné", - "Resilio-sync (formerly BitTorrent Sync) uses the BitTorrent protocol to sync files and folders between all of your devices. There are both free and paid versions, this container supports both. There is an official sync image but we created this one as it supports user mapping to simplify permissions for volumes.": "Resilio-sync (predtým BitTorrent Sync) používa protokol BitTorrent na synchronizáciu súborov a priečinkov medzi všetkými zariadeniami. Existujú ako zadarmo a platená verzia, tento kontajner podporuje oboje. Existuje oficiálna synchronizácia obrazu, ale my sme vytvorili tento, pretože podporuje mapovanie užívateľa na zjednodušenie oprávnení pre objemy.", + "Resilio-sync (formerly BitTorrent Sync) uses the BitTorrent protocol to sync files and folders between all of your devices. There are both free and paid versions, this container supports both. There is an official sync image but we created this one as it supports user mapping to simplify permissions for volumes.": "Resilio Sync (predtým BitTorrent Sync) používa protokol BitTorrent na synchronizáciu súborov a priečinkov medzi zariadeniami. Kontajner podporuje bezplatnú aj platenú verziu. Oproti oficiálnemu obrazu podporuje mapovanie používateľov, ktoré zjednodušuje oprávnenia dátových zväzkov.", "Resolve package conflicts": "Vyriešiť konflikty balíkov", "Resources": "Zdroje", "Restart Network": "Reštartovať sieť", "Restart Network Service": "Reštartovať sieťovú službu", "Restart Web UI proxy": "Reštartovať proxy webového rozhrania", - "Restart aMule automatically on a schedule (true or false)": "Reštartujte aMule automaticky podľa plánu (pravda alebo nepravda)", + "Restart aMule automatically on a schedule (true or false)": "Automaticky reštartovať aMule podľa plánu (true alebo false)", "Restart and enable Samba:": "Reštartovať a zapnúť Sambu:", "Restart container to activate mount?": "Reštartovať kontajner, aby sa mount aktivoval?", "Restarting ProxMenux Monitor service...": "Reštartujem službu ProxMenux Monitor...", @@ -5354,14 +5357,14 @@ "Results:": "Výsledky:", "Retention": "Uchovávanie", "Retention policy (prune-backups syntax).": "Zásady uchovávania (syntax orezaných záloh).", - "RetroArch is a frontend for emulators, game engines and media players. It enables you to run classic games on a wide range of computers and consoles through its slick graphical interface.": "RetroArch je frontend pre emulátory, herné motory a prehrávače médií. Umožňuje spustiť klasické hry na širokej škále počítačov a konzol prostredníctvom svojho klik grafického rozhrania.", + "RetroArch is a frontend for emulators, game engines and media players. It enables you to run classic games on a wide range of computers and consoles through its slick graphical interface.": "RetroArch je grafické rozhranie pre emulátory, herné enginy a prehrávače médií. Umožňuje spúšťať klasické hry na širokej škále počítačov a konzol prostredníctvom prehľadného grafického rozhrania.", "Return": "Späť", "Return to Main Menu": "Späť do hlavného menu", "Return to Share Menu": "Späť do menu zdieľania", "Return to main menu": "Späť do hlavného menu", "Returning the containers to their previous state...": "Vraciam kontajnery do ich predchádzajúceho stavu...", "Reused the encryption key from the PVE storage entry.": "Použil sa šifrovací kľúč z položky PVE úložiska.", - "Reverse proxy samples for other applications are in /config/nginx/proxy_confs inside the container.": "Reverzné proxy vzorky pre iné aplikácie sú v /config/nginx/proxy confs vnútri kontajnera.", + "Reverse proxy samples for other applications are in /config/nginx/proxy_confs inside the container.": "Príklady reverzného proxy pre ďalšie aplikácie sú v /config/nginx/proxy_confs v kontajneri.", "Reverting AMD (Ryzen/EPYC) fixes...": "Vraciam späť opravy pre AMD (Ryzen/EPYC)...", "Reverting IOMMU/VFIO configuration...": "Vraciam späť nastavenie IOMMU/VFIO...", "Reverting TCP BBR + Fast Open...": "Vraciam späť TCP BBR + Fast Open...", @@ -5374,10 +5377,10 @@ "Review package upgrade": "Skontrolovať aktualizáciu balíkov", "Review passthrough config files": "Skontrolovať konfiguračné súbory passthrough", "Review what will be removed": "Skontrolujte, čo sa odstráni", - "Rip DVD and Blu-ray media from a browser": "Rip DVD a Blu-ray médiá z prehliadača", + "Rip DVD and Blu-ray media from a browser": "Kopírovanie médií DVD a Blu-ray cez prehliadač", "Rollback: nothing to remove (host matches backup)": "Rollback: nie je čo odstrániť (host zodpovedá zálohe)", "Rolling back the incomplete container": "Zatáčanie nedokončenej nádoby", - "RomM is a self-hosted ROM manager for managing and playing game collections.": "RomM je self-hosted ROM manažér pre správu a hranie zbierky hier.", + "RomM is a self-hosted ROM manager for managing and playing game collections.": "RomM je správca obrazov ROM na vlastnom serveri na správu a hranie herných zbierok.", "Root SSH keys/config": "SSH kľúče/nastavenie roota", "Root inside container = root on host system": "Root v kontajneri = root na hostiteľskom systéme", "Root privileges are required": "Vyžadujú sa oprávnenia používateľa root", @@ -5388,10 +5391,10 @@ "Rotate the recovery passphrase": "Zmeniť obnovovaciu frázu", "Routing Information": "Informácie o smerovaní", "Routing Table": "Smerovacia tabuľka", - "Rsnapshot is a filesystem snapshot utility based on rsync. rsnapshot makes it easy to make periodic snapshots of local machines, and remote machines over ssh. The code makes extensive use of hard links whenever possible, to greatly reduce the disk space required.": "Rsnapshot je nástroj pre snímky súborového systému založený na rync. rsnapshot uľahčuje vytváranie pravidelných snímok lokálnych strojov a diaľkových strojov nad ssh. Kód robí rozsiahle použitie tvrdých odkazov vždy, keď je to možné, výrazne znížiť miesto na disku required.", + "Rsnapshot is a filesystem snapshot utility based on rsync. rsnapshot makes it easy to make periodic snapshots of local machines, and remote machines over ssh. The code makes extensive use of hard links whenever possible, to greatly reduce the disk space required.": "Rsnapshot vytvára snímky súborového systému pomocou rsync. Uľahčuje pravidelné zálohovanie lokálnych aj vzdialených počítačov cez SSH. Všade, kde je to možné, používa pevné odkazy, čím výrazne znižuje potrebné miesto na disku.", "Run 'Mount NFS Share' to install NFS client automatically.": "Spustite 'Pripojiť NFS zdieľanie', aby sa NFS klient nainštaloval automaticky.", "Run 'Mount Samba Share' to install CIFS client automatically.": "Spustite 'Pripojiť Samba zdieľanie', aby sa CIFS klient nainštaloval automaticky.", - "Run GGUF LLMs locally with GPU acceleration": "Spustiť GGUF LLM lokálne s GPU zrýchlenia", + "Run GGUF LLMs locally with GPU acceleration": "Lokálne spúšťanie jazykových modelov GGUF so zrýchlením pomocou GPU", "Run Now": "Spustiť teraz", "Run PVE 8 to 9": "Spustiť PVE 8 na 9", "Run PVE 8 to 9 check": "Spustiť kontrolu PVE 8 na 9", @@ -5427,7 +5430,7 @@ "Running dkms autoinstall for kernel": "Spúšťam dkms autoinstall pre kernel", "Running kernel:": "Spustené jadro:", "Running pre-upgrade simulation to verify 'proxmox-ve' will remain installed...": "Spúšťam simuláciu pred aktualizáciou, aby sa overilo, že 'proxmox-ve' zostane nainštalované...", - "RustDesk is a full-featured open source remote control alternative for self-hosting and security with minimal configuration.": "RustDesk je plne funkčné open source diaľkové ovládanie alternatívu pre self-hosting a bezpečnosť s minimálnou konfiguráciou.", + "RustDesk is a full-featured open source remote control alternative for self-hosting and security with minimal configuration.": "RustDesk je plnohodnotný nástroj na vzdialené ovládanie s otvoreným zdrojovým kódom, vlastným serverom, dôrazom na bezpečnosť a minimálnym nastavovaním.", "SATA (standard - high compatibility)": "SATA (štandard - vysoká kompatibilita)", "SCSI (recommended for Linux and Windows)": "SCSI (odporúčané pre Linux a Windows)", "SCSI (recommended for Linux)": "SCSI (odporúčané pre Linux)", @@ -5460,8 +5463,8 @@ "STEP 9: Cleanup (LVM only)": "KROK 9: čistenie (iba LVM)", "STORAGE TYPE IDENTIFICATION:": "ROZPOZNANIE TYPU ÚLOŽISKA:", "SUGGESTION FOR": "NÁVRH PRE", - "SWAG serves HTTPS on port 443. Plain HTTP on port 80 is disabled in /config/nginx/site-confs/default.conf.": "SWAG slúži HTTPS na porte 443. Plain HTTP na porte 80 je vypnutý v /config/nginx/site-confs/default.conf.", - "Sabnzbd makes Usenet as simple and streamlined as possible by automating everything we can. All you have to do is add an .nzb. SABnzbd takes over from there, where it will be automatically downloaded, verified, repaired, extracted and filed away with zero human interaction.": "Sabnzbd robí Usenet tak jednoduché a efektívne, ako je to možné automatizáciou všetko, čo môžeme. Všetko, čo musíte urobiť, je pridať .nzb. SABnzbd preberá odtiaľ, kde bude automaticky stiahnutá, overená, opravená, extrahovaná a podaná preč s nulovou ľudskou interakciou.", + "SWAG serves HTTPS on port 443. Plain HTTP on port 80 is disabled in /config/nginx/site-confs/default.conf.": "SWAG poskytuje HTTPS na porte 443. Nešifrované HTTP na porte 80 je vypnuté v /config/nginx/site-confs/default.conf.", + "Sabnzbd makes Usenet as simple and streamlined as possible by automating everything we can. All you have to do is add an .nzb. SABnzbd takes over from there, where it will be automatically downloaded, verified, repaired, extracted and filed away with zero human interaction.": "SABnzbd zjednodušuje používanie Usenetu automatizáciou všetkých krokov. Stačí pridať súbor .nzb a SABnzbd obsah automaticky stiahne, overí, opraví, rozbalí a uloží bez ďalšieho zásahu používateľa.", "Safe design: no automatic ACL/ownership mutation on host or CT.": "Bezpečný návrh: bez automatickej zmeny ACL alebo vlastníctva na hostovi či v CT.", "Safe to apply now": "Bezpečné použiť teraz", "Safety Backup": "Bezpečnostná záloha", @@ -5542,11 +5545,11 @@ "Scheduler script not found:": "Skript plánovača sa nenašiel:", "ScreenScraper": "ScreenScraper", "ScreenScraper password": "Heslo ScreenScrapera", - "ScreenScraper username": "Užívateľské meno screenScrapera", + "ScreenScraper username": "Používateľské meno ScreenScraper", "Script Information": "Informácie o skripte", "Script not found:": "Skript sa nenašiel:", "Scripts in": "Skripty v kategórii", - "ScummVM is a program which allows you to run certain classic graphical adventure and role-playing games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed! ScummVM is a complete rewrite of these games' executables and is not an emulator.": "ScummVM je program, ktorý vám umožní spustiť určité klasické grafické dobrodružstvo a hranie rolí za predpokladu, že už máte ich dátové súbory. Inteligentná časť o tom: ScummVM práve nahrádza spustiteľné materiály dodávané s hrami, čo vám umožní hrať je na systémoch, pre ktoré neboli nikdy navrhnuté! ScummVM je kompletný prepis týchto hier 'spustiteľné a nie je emulátor.", + "ScummVM is a program which allows you to run certain classic graphical adventure and role-playing games, provided you already have their data files. The clever part about this: ScummVM just replaces the executables shipped with the games, allowing you to play them on systems for which they were never designed! ScummVM is a complete rewrite of these games' executables and is not an emulator.": "ScummVM umožňuje spúšťať vybrané klasické grafické adventúry a hry na hrdinov, ak už máte ich dátové súbory. Nahrádza pôvodné spustiteľné súbory hier, takže ich možno hrať aj na systémoch, pre ktoré neboli navrhnuté. Je úplným prepisom ich herných programov, nie emulátorom.", "Sealskin is a self-hosted, client-server platform that enables users to run powerful, containerized desktop applications streamed directly to a web browser. It uses a browser extension to intercept user actions—such as clicking a link or downloading a file and redirects them to a secure, isolated application environment running on a remote server.": "Sealskin je self-hosted, klient-server platforma, ktorá umožňuje užívateľom spustiť výkonné, kontajnerové desktopové aplikácie streamované priamo do webového prehliadača. Používa rozšírenie prehliadača na zachytenie užívateľských akcií, ako je kliknutie na odkaz alebo stiahnutie súboru a presmeruje ich na bezpečné, izolované aplikačné prostredie bežiace na vzdialenom serveri.", "Search": "Hľadať", "Search Results for:": "Výsledky hľadania pre:", @@ -5563,7 +5566,7 @@ "Security Updates": "Bezpečnostné aktualizácie", "Security Warning — read before applying": "Bezpečnostné upozornenie - prečítajte pred použitím", "Security directive outside the dynamic profile": "Bezpečnostná smernica mimo dynamického profilu", - "Security relaxation declined": "Uvoľnenie zabezpečenia sa znížilo", + "Security relaxation declined": "Uvoľnenie bezpečnostných obmedzení bolo odmietnuté", "See": "Pozrite", "See /tmp/proxmenux-mount.log for details.": "Podrobnosti nájdete v /tmp/proxmenux-mount.log.", "Seerr WebUI": "Seerr WebUI", @@ -5632,7 +5635,7 @@ "Select archive to restore": "Vyberte archív na obnovu", "Select at least one path to continue.": "Pre pokračovanie vyberte aspoň jednu cestu.", "Select at least one stack container": "Vyberte aspoň jeden kontajner aplikácie", - "Select at least one suite application": "Vyberte aspoň jednu aplikáciu suite", + "Select at least one suite application": "Vyberte aspoň jednu aplikáciu zostavy", "Select authentication mode:": "Vyberte spôsob prihlásenia:", "Select authentication type:": "Vyberte typ prihlásenia:", "Select available Controllers/NVMe to add:": "Vyberte dostupné Controllery/NVMe na pridanie:", @@ -5743,18 +5746,18 @@ "Selected paths produced no entries to apply.": "Vybrané cesty nevytvorili žiadne položky na použitie.", "Selected utilities installation completed": "Inštalácia vybraných nástrojov je dokončená", "Selection": "Výber", - "Self-custodial Bitcoin Lightning wallet with integrated node and app connections.": "Samoobsluha Bitcoin Lightning peňaženka s integrovaným uzlom a pripojenie aplikácií.", - "Self-hosted ZeroTier network controller with web UI for centralized management.": "Samo-hostiteľom ZeroTier sieťový regulátor s web UI pre centralizované riadenie.", - "Self-hosted cloud data migration & sync manager": "Self-hosted cloud dát Migrácia a synchronizácia manažéra", - "Self-hosted collaborative bookmark manager to collect, read, annotate, and fully preserve what matters, all in one place.": "Self-hosted kolaboratívne záložky manažér zbierať, čítať, anotovať, a plne zachovať to, na čom záleží, všetko na jednom mieste.", - "Self-hosted file sharing with a modern web interface": "Samoobsluha zdieľania súborov s moderným webovým rozhraním", - "Self-hosted file toolkit for images, video, audio, PDFs, and files": "Súborový súbor s vlastným hostovaním pre obrázky, video, audio, PDF a súbory", - "Self-hosted internet archiving solution": "Samoobslužné riešenie archivácie internetu", - "Self-hosted recipe manager and meal planner": "Self-hosted recept manažér a jedlo plánovač", + "Self-custodial Bitcoin Lightning wallet with integrated node and app connections.": "Peňaženka Bitcoin Lightning s vlastnou správou prostriedkov, integrovaným uzlom a pripojením aplikácií.", + "Self-hosted ZeroTier network controller with web UI for centralized management.": "Sieťový kontrolér ZeroTier na vlastnom serveri s webovým rozhraním na centrálnu správu.", + "Self-hosted cloud data migration & sync manager": "Správca migrácie a synchronizácie cloudových údajov na vlastnom serveri", + "Self-hosted collaborative bookmark manager to collect, read, annotate, and fully preserve what matters, all in one place.": "Spoločný správca záložiek na vlastnom serveri na zhromažďovanie, čítanie, komentovanie a úplné uchovávanie dôležitého obsahu na jednom mieste.", + "Self-hosted file sharing with a modern web interface": "Zdieľanie súborov na vlastnom serveri s moderným webovým rozhraním", + "Self-hosted file toolkit for images, video, audio, PDFs, and files": "Súprava nástrojov na vlastnom serveri na prácu s obrázkami, videom, zvukom, PDF a ďalšími súbormi", + "Self-hosted internet archiving solution": "Riešenie na archiváciu internetového obsahu na vlastnom serveri", + "Self-hosted recipe manager and meal planner": "Správca receptov a plánovač jedál na vlastnom serveri", "Self-hosted software development service": "Služba na vývoj softvéru s vlastným hostovaním", "Self-signed TLS certificate created:": "Vytvorený samopodpísaný certifikát TLS:", - "Selfhosted PDF manager, viewer and editor": "Selfhosted PDF manažér, prehliadač a editor", - "Selkies desktop and streaming acceleration": "Selkies plocha a streamovanie zrýchlenie", + "Selfhosted PDF manager, viewer and editor": "Správca, prehliadač a editor PDF na vlastnom serveri", + "Selkies desktop and streaming acceleration": "Zrýchlenie pracovnej plochy a streamovania Selkies", "Sending backup to Borg repository...": "Odosielam zálohu do Borg repozitára...", "Sending backup to PBS...": "Odosielam zálohu do PBS...", "Server": "Server", @@ -5821,7 +5824,7 @@ "Share added successfully.": "Zdieľanie bolo úspešne pridané.", "Share deleted and Samba service restarted.": "Zdieľanie bolo vymazané a služba Samba reštartovaná.", "Share exists:": "Zdieľanie existuje:", - "Share folders automatically (true or false)": "Automatické zdieľanie priečinkov (pravda alebo nepravda)", + "Share folders automatically (true or false)": "Automaticky zdieľať priečinky (true alebo false)", "Share name:": "Názov zdieľania:", "Share not found on server:": "Zdieľanie sa na serveri nenašlo:", "Share path:": "Cesta zdieľania:", @@ -5839,11 +5842,11 @@ "Shared host content (not included in LXC backups):": "Zdieľaný obsah hostiteľa (nezahrnutý do záloh LXC):", "Shared host data is not reverted by the backup. Continue?": "Zdieľané dáta hostiteľa nie sú vrátené zálohou. Pokračovať?", "Shared host data is not reverted by the backups. Continue?": "Zdieľané dáta hostiteľa nie sú vrátené zálohami. Pokračovať?", - "Shared host directories (not included in Proxmox backups)": "Zdieľané adresáre hostiteľov (nezahrnuté do záloh Proxmox)", + "Shared host directories (not included in Proxmox backups)": "Zdieľané adresáre hostiteľa (nie sú zahrnuté do záloh Proxmoxu)", "Shared host directory": "Zdieľaný adresár hostiteľa", "Shared host directory (not included in Proxmox backups)": "Zdieľaný adresár hostiteľa (nie je v zálohách Proxmoxu)", "Shared host files are kept as they are; the backup does not restore their content.": "Zdieľané hostiteľské súbory sú uchovávané ako sú; záloha neobnovuje ich obsah.", - "Shared host media directory": "Zdieľaný adresár médií pre hostiteľov", + "Shared host media directory": "Zdieľaný adresár médií na hostiteľovi", "Shared memory size for the GPU workload in MB": "Zdieľaná veľkosť pamäte pre pracovné zaťaženie GPU v MB", "Sharedfiles group already exists (GID: 101000)": "Skupina sharedfiles už existuje (GID: 101000)", "Shares found:": "Nájdené zdieľania:", @@ -5902,13 +5905,13 @@ "Shows status and type (nfs/cifs/dir/iscsi...).": "Zobrazí stav a typ (nfs/cifs/dir/iscsi...).", "Shutdown timeout": "Čas na vypnutie vypršal", "SiYuan access code": "Prístupový kód SiYuan", - "SickGear provides management of TV shows and/or Anime, it detects new episodes, links downloader apps, and more..": "SickGear poskytuje správu televíznych relácií a / alebo Anime, detekuje nové epizódy, odkazy aplikácie pre downloader, a ďalšie..", + "SickGear provides management of TV shows and/or Anime, it detects new episodes, links downloader apps, and more..": "SickGear spravuje televízne seriály a anime, rozpoznáva nové epizódy a prepája sa s aplikáciami na sťahovanie.", "Sign in to a registry": "Prihlásiť sa do registra", "Sign in to the panel and create a Samba user in Settings before publishing a directory over SMB.": "Pred publikovaním adresára cez SMB sa prihláste na panel a vytvorte používateľa Samba v Nastaveniach.", "Signal is a messaging app with privacy at its core. It is free and easy to use, with strong end-to-end encryption that keeps your communication completely private.": "Signal je aplikácia správ s ochranou súkromia v jej jadre. Je zadarmo a ľahko použiteľný, so silným end-to-end šifrovaním, ktoré udržuje vašu komunikáciu úplne súkromnú.", "Signatures removed. Partition table preserved.": "Podpisy boli odstránené. Tabuľka oddielov zostala zachovaná.", "Signed in to {registry} as {username}.\n\nCredentials are saved for ProxMenux.\nSelect the account to check, download or install an image.": "Prihlásenie do {registry} ako {username} bolo úspešné.\n\nPrihlasovacie údaje sú uložené pre ProxMenux.\nVyberte účet, cez ktorý chcete overiť prístup k obrazu, stiahnuť ho alebo ho nainštalovať.", - "Simple and easy to use DDNS": "Jednoduché a jednoduché použitie DDNS", + "Simple and easy to use DDNS": "Jednoducho použiteľné dynamické DNS (DDNS)", "Single GPU Warning": "Upozornenie na jednu GPU", "Single target found — selected automatically:": "Našiel sa jeden target - vybraný automaticky:", "Size": "Veľkosť", @@ -5935,9 +5938,9 @@ "Skipping manual patches — feranick fork already supports this kernel.": "Ručné záplaty preskakujem - fork feranick už tento kernel podporuje.", "Skopeo is not available. Restore the ProxMenux OCI dependencies and try again.": "Skopeo nie je k dispozícii. Obnovte závislosti ProxMenux OCI a skúste to znova.", "Skopeo returned an invalid credential file; saved credentials were not changed.": "Skopeo vrátilo neplatný súbor s prihlasovacími údajmi; uložené prihlasovacie údaje sa nezmenili.", - "Sleek podcast downloader with GPodder sync": "Sleek podcast downloader s GPodder synchronizácia", + "Sleek podcast downloader with GPodder sync": "Prehľadný nástroj na sťahovanie podcastov so synchronizáciou GPodder", "Smart restore plan — hardware compatibility check": "Inteligentný plán obnovy - kontrola kompatibility hardvéru", - "Smokeping keeps track of your network latency. For a full example of what this application is capable of visit UCDavis.": "Smokeping sleduje latenciu vašej siete. Úplný príklad toho, čo je táto aplikácia schopná navštíviť UCDavis.", + "Smokeping keeps track of your network latency. For a full example of what this application is capable of visit UCDavis.": "SmokePing sleduje latenciu siete. Ukážku jeho možností nájdete na stránke UCDavis.", "SnapOtter": "SnapOtter", "Snapshot - no downtime (recommended)": "Snímka – žiadne prestoje (odporúča sa)", "Snippets — hook scripts / config": "Snippets — hook skripty / konfigurácia", @@ -5959,7 +5962,7 @@ "Some utility packages could not be removed; the remaining list has been preserved": "Niektoré balíky nástrojov nebolo možné odstrániť;zostávajúci zoznam sa zachoval", "Something is already mounted at": "Niečo už je pripojené v", "Something is already mounted at:": "Niečo už je pripojené v:", - "Sonarr (formerly NZBdrone) is a PVR for usenet and bittorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.": "Sonarr (predtým NZBdrone) je PVR pre užívateľov usenet a bittorrent. To môže sledovať viac RSS kanálov pre nové epizódy vašich obľúbených show a bude chytiť, triediť a premenovať ich. Môže byť tiež nakonfigurovaný tak, aby automaticky upgrade kvality súborov už stiahnutých, keď je k dispozícii kvalitnejší formát.", + "Sonarr (formerly NZBdrone) is a PVR for usenet and bittorrent users. It can monitor multiple RSS feeds for new episodes of your favorite shows and will grab, sort and rename them. It can also be configured to automatically upgrade the quality of files already downloaded when a better quality format becomes available.": "Sonarr (predtým NZBdrone) je správca seriálov pre používateľov Usenetu a BitTorrentu. Sleduje RSS kanály s novými epizódami obľúbených seriálov a automaticky ich získava, triedi a premenúva. Dokáže tiež nahradiť už stiahnuté súbory kvalitnejšou verziou, keď sa objaví.", "Sonarr added to Prowlarr": "Sonarr pridaný do Prowlarr", "Sonarr connected to qBittorrent": "Sonarr napojený na qBittorrent", "Sonarr root folder configured": "Konfigurovaný koreňový priečinok Sonarr", @@ -5972,8 +5975,8 @@ "Specific host (enter IP)": "Konkrétny host (zadať IP)", "Specific subnet (enter manually)": "Konkrétna podsieť (zadať ručne)", "Speedtest Tracker web interface": "Webové rozhranie Speedtest Tracker", - "Speedtest-tracker is a self-hosted internet performance tracking application that runs speedtest checks against Ookla's Speedtest service.": "Speedtest-tracker je self-hosted internetové sledovanie výkonu aplikácie, ktorá beží rýchlostnú kontrolu proti Ookla je Speedtest služby.", - "Spotube is an open source, cross-platform Spotify client compatible across multiple platforms utilizing Spotify's data API and YouTube, Piped.video or JioSaavn as an audio source, eliminating the need for Spotify Premium": "Spotube je open source, cross-platform Spotify klient kompatibilný vo viacerých platformách využívajúce dáta spoločnosti Spotify API a YouTube, Piped. video alebo JioSaavn ako audio zdroj, odstránenie potreby Spotify Premium", + "Speedtest-tracker is a self-hosted internet performance tracking application that runs speedtest checks against Ookla's Speedtest service.": "Speedtest Tracker je aplikácia na vlastnom serveri na sledovanie výkonu internetového pripojenia pomocou meraní služby Ookla Speedtest.", + "Spotube is an open source, cross-platform Spotify client compatible across multiple platforms utilizing Spotify's data API and YouTube, Piped.video or JioSaavn as an audio source, eliminating the need for Spotify Premium": "Spotube je multiplatformový klient Spotify s otvoreným zdrojovým kódom. Využíva dátové API Spotify a zvuk získava z YouTube, Piped.video alebo JioSaavn, takže nevyžaduje Spotify Premium.", "Stable (main branch)": "Stabilná verzia (vetva main)", "Stable monitor service normalized.": "Služba Monitora pre stabilnú verziu je upravená.", "Stack": "Stack", @@ -5982,7 +5985,7 @@ "Stack checked:": "Aplikácia je skontrolovaná:", "Stack members are missing; recreate them after verifying their volumes": "Chýbajú kontajnery aplikácie; znova ich vytvorte až po overení ich dátových zväzkov", "Stack members are updated together with their stack": "Členovia Stack sú aktualizované spolu s ich stack", - "Stack name": "Názov zásobníka", + "Stack name": "Názov zostavy", "Stack records restored": "Záznamy všetkých kontajnerov aplikácie sú obnovené", "Stack records saved": "Záznamy kontajnerov aplikácie sú uložené", "Stack records saved; no container was reinstalled.": "Stack záznamy uložené; žiadny kontajner bol znovu nainštalovaný.", @@ -6005,7 +6008,7 @@ "Start VM after creation?": "Spustiť VM po vytvorení?", "Start a container. Use the correct ": "Spustiť kontajner. Použite správne ", "Start a virtual machine. Use the correct ": "Spustiť VM. Použite správne ", - "Start each LXC with Proxmox (no coordinated startup)": "Začnite každý LXC s Proxmox (bez koordinovaného štartu)", + "Start each LXC with Proxmox (no coordinated startup)": "Spúšťať každý kontajner LXC spolu s Proxmoxom (bez koordinovaného spustenia)", "Start long self-test (hours)": "Spustiť dlhý samotest (hodiny)", "Start long test now?": "Spustiť dlhý test teraz?", "Start on boot already disabled for VM": "Štart pri boote je už pre VM vypnutý", @@ -6025,7 +6028,7 @@ "Start the converted container:": "Spustiť prevedený kontajner:", "Start the interactive Proxmox package upgrade": "Spustiť interaktívnu aktualizáciu balíkov Proxmoxu", "Start the main system upgrade:": "Spustite hlavnú aktualizáciu systému:", - "Start the stack with Proxmox": "Spustiť zásobník s Proxmox", + "Start the stack with Proxmox": "Spúšťať zostavu spolu s Proxmoxom", "Start uploading to PBS — sets a recovery passphrase": "Spustiť nahrávanie do PBS - nastaví obnovovaciu frázu", "Start when finished": "Spustiť po dokončení", "Start with Proxmox": "Spúšťať spolu so spustením Proxmoxu", @@ -6115,17 +6118,17 @@ "Storage for Tandoor files": "Úložisko pre súbory Tandoor", "Storage for persistent data": "Skladovanie trvalých údajov", "Storage for recipe images and files": "Skladovanie pre recepty a súbory", - "Storage for rootfs": "Skladovanie pre rootfs", - "Storage for rootfs and private configuration": "Skladovanie pre rootfs a súkromné konfigurácie", + "Storage for rootfs": "Úložisko pre koreňový súborový systém", + "Storage for rootfs and private configuration": "Úložisko pre koreňový súborový systém a súkromné nastavenia", "Storage for staticfiles": "Úložisko pre statické súbory", - "Storage for the Immich library": "Sklad pre knižnicu Immich", - "Storage for the Nextcloud data": "Skladovanie údajov Nextcloud", + "Storage for the Immich library": "Úložisko pre knižnicu Immich", + "Storage for the Nextcloud data": "Úložisko pre údaje Nextcloud", "Storage for the OCI image cache": "Úložisko pre vyrovnávaciu pamäť obrazu OCI", "Storage for the backup of this operation": "Úložisko pre zálohu tejto operácie", "Storage for the consume and export folders": "Skladovanie pre konzumné a exportné priečinky", "Storage for the container and its data": "Úložisko pre kontajner a jeho údaje", "Storage for the containers and their data": "Úložisko pre kontajnery a ich údaje", - "Storage for the persistent configuration": "Skladovanie pre trvalú konfiguráciu", + "Storage for the persistent configuration": "Úložisko pre trvalé nastavenia", "Storage is now available in Proxmox web interface under Datacenter > Storage": "Úložisko je teraz dostupné vo webovom rozhraní Proxmoxu v časti Datacenter > Storage", "Storage plan selection cancelled.": "Výber plánu úložiska bol zrušený.", "Storage plan selection failed or cancelled": "Výber plánu úložiska zlyhal alebo bol zrušený", @@ -6134,7 +6137,7 @@ "Stored Credentials:": "Uložené prihlasovacie údaje:", "Stored credentials:": "Uložené prihlasovacie údaje:", "Stremio is a modern media center that gives you the freedom to watch everything you want.": "Stremio je moderné mediálne centrum, ktoré vám dáva slobodu sledovať všetko, čo chcete.", - "Subdomains for the certificate, comma separated (wildcard for *.domain)": "Subdomény pre osvedčenie, čiarka oddelené (divoká karta pre *.doména)", + "Subdomains for the certificate, comma separated (wildcard for *.domain)": "Subdomény pre certifikát oddelené čiarkami (wildcard pre *.domain)", "Subnet": "Podsieť", "Subscription banner removal failed": "Odstránenie bannera odberu zlyhalo", "Subscription banner removed successfully": "Banner predplatného bol úspešne odstránený", @@ -6149,7 +6152,7 @@ "Swap in MB": "Swap v MB", "Swap partition detected": "Zistený swap oddiel", "Swappiness configuration created successfully": "Nastavenie swappiness bolo úspešne vytvorené", - "Swing Music is a beautifully designed, self-hosted music streaming server. Like a cooler Spotify ... but bring your own music.": "Swing Music je krásne navrhnutý, self-hosted hudobný streaming server. Ako cooler Spotify ... ale priniesť si vlastnú hudbu.", + "Swing Music is a beautifully designed, self-hosted music streaming server. Like a cooler Spotify ... but bring your own music.": "Swing Music je hudobný streamovací server s pekným rozhraním, ktorý si môžete prevádzkovať sami. Podobá sa Spotify, ale používa vašu vlastnú hudobnú zbierku.", "Switch GPU Mode (VM <-> LXC)": "Prepnúť režim GPU (VM <-> LXC)", "Switch Mode": "Zmena režimu", "Switch Script Not Found": "Skript na prepnutie sa nenašiel", @@ -6216,7 +6219,7 @@ "Target server:": "Cieľový server:", "Target:": "Target:", "Tautulli is a python based web application for monitoring, analytics and notifications for Plex Media Server.": "Tautulli je webová aplikácia založená na pythone pre monitorovanie, analýzu a oznámenia pre Plex Media Server.", - "Teable adopts a concise spreadsheet interface, yet creates powerful database applications": "Teable prijíma stručné tabuľkové rozhranie, ale vytvára výkonné databázové aplikácie", + "Teable adopts a concise spreadsheet interface, yet creates powerful database applications": "Teable umožňuje vytvárať výkonné databázové aplikácie cez jednoduché tabuľkové rozhranie", "Telegram is a cloud-based mobile and desktop messaging app.": "Telegram je mobilná a desktopová aplikácia na báze cloudových správ.", "Temporary data container:": "Dočasný dátový kontajner:", "Temporary login": "Dočasné prihlásenie", @@ -6240,14 +6243,14 @@ "Testing connectivity to portal...": "Testujem pripojenie k portálu...", "Testing guest listing and share access on server": "Testovanie zoznamu hostí a zdieľania prístupu na serveri", "Testing network connectivity...": "Testujem sieťové pripojenie...", - "Text that new pads start with (empty = the text of the image)": "Text, s ktorým začínajú nové podložky (prázdne = text obrázku)", + "Text that new pads start with (empty = the text of the image)": "Počiatočný text nových dokumentov (prázdne = text dodaný v obraze)", "Thank you for using ProxMenux. Goodbye!": "Ďakujeme, že používate ProxMenux. Dovidenia!", "That VM is currently stopped, so the GPU can be reassigned now.": "Tá VM je momentálne zastavená, takže GPU sa teraz dá presunúť.", "That doesn't look like an SSH private key. Pick the private key file (no .pub extension, parseable by ssh-keygen).": "Toto nevyzerá ako súkromný SSH kľúč. Vyberte súbor súkromného kľúča (bez prípony .pub, čitateľný cez ssh-keygen).", "The AMD GPU did not complete a test inference with ROCm": "AMD GPU nedokončil testovanie s ROCm", "The AMD driver does not offer its compute interface (/dev/kfd) on this host.": "Ovládač AMD neponúka svoje výpočtové rozhranie (/dev/kfd) na tomto hostiteľovi.", "The AppArmor/seccomp relaxation does not include the required consent": "AppArmor/seccomp relaxácia nezahŕňa súhlas required", - "The Bookmark Everything App": "Záložka Všetko App", + "The Bookmark Everything App": "Aplikácia na ukladanie záložiek všetkého druhu", "The Brave browser is a fast, private and secure web browser for PC, Mac and mobile.": "Prehliadač Brave je rýchly, súkromný a bezpečný webový prehliadač pre PC, Mac a mobil.", "The CT already exists:": "CT už existuje:", "The Compose file declares no service": "Súbor Compose neobsahuje žiadnu službu", @@ -6255,7 +6258,7 @@ "The Compose file describes several services:": "Súbor Compose opisuje viacero služieb:", "The Compose file does not contain a Compose document": "Súbor neobsahuje dokument Compose", "The Compose file is not valid YAML:": "Súbor Compose nie je platný YAML:", - "The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. Continue only if you trust the image and accept this risk.": "Skladba ponúka voliteľnú AppArmor alebo seccomp relaxáciu; zostane vypnutá, ak si ju užívateľ nevyberie. Pokračujte len vtedy, ak dôverujete obrazu a prijmete toto riziko.", + "The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. Continue only if you trust the image and accept this risk.": "Súbor Compose ponúka voliteľné uvoľnenie obmedzení AppArmor alebo seccomp, ktoré zostane vypnuté, kým ho používateľ nevyberie. Pokračujte iba vtedy, ak obrazu dôverujete a riziko prijímate.", "The Compose value must be text or a list:": "Kompozitná hodnota musí byť text alebo zoznam:", "The DRM node is not an Intel or AMD GPU; NVIDIA requires its library profile": "DRM uzol nie je Intel ani AMD GPU; NVIDIA requires svoj profil knižnice", "The Entrypoint/Cmd combination is empty": "Vstupný bod/Cmd combination je prázdny", @@ -6275,7 +6278,7 @@ "The LXC has no Bash to run this script with. The addon was not run.": "LXC nemá Bash na spustenie tohto skriptu. Doplnok nebol spustený.", "The LXC has stopped": "LXC zastavil", "The LXC is stopped. Do you want to start it to run the addon?": "LXC je zastavený. Chcete ho spustiť, aby sa spustil doplnok?", - "The Lounge starts in public mode: anyone who reaches the address opens the client without logging in, and the IRC networks added are lost when the session ends.": "Lounge začína vo verejnom režime: každý, kto dosiahne adresu otvorí klienta bez prihlásenia, a IRC siete pridané sú stratené po skončení sedenia.", + "The Lounge starts in public mode: anyone who reaches the address opens the client without logging in, and the IRC networks added are lost when the session ends.": "The Lounge začína vo verejnom režime: každý s prístupom na adresu otvorí klienta bez prihlásenia. Pridané siete IRC sa po skončení relácie stratia.", "The MAC address of the container cannot be kept": "Adresa MAC kontajnera sa nemôže uchovávať", "The NVIDIA Container Toolkit repository definition was empty.": "Definícia úložiska NVIDIA Container Toolkit bola prázdna.", "The NVIDIA Container Toolkit signing key could not be read.": "Nepodarilo sa prečítať podpisový kľúč súpravy NVIDIA Container Toolkit.", @@ -6293,7 +6296,7 @@ "The OCI archive does not contain exactly one manifest": "Archív OCI neobsahuje presne jeden manifest", "The OCI archive does not exist or is empty:": "Archív OCI neexistuje alebo je prázdny:", "The OCI archive verifier was not found": "Archívový overovateľ OCI nebol nájdený", - "The OCI catalog is not installed. Update ProxMenux and try again.": "Katalóg OCI nie je nainštalovaný. Aktualizovať ProxMenux a skúste to znova.", + "The OCI catalog is not installed. Update ProxMenux and try again.": "Katalóg OCI nie je nainštalovaný. Aktualizujte ProxMenux a skúste to znova.", "The OCI contract changed while reviewing; nothing was adopted": "Zmluva OCI sa zmenila počas kontroly;nič nebolo prijaté", "The OCI engine is not installed. Update ProxMenux and try again.": "Motor OCI nie je nainštalovaný. Aktualizovať ProxMenux a skúste to znova.", "The OCI image storage was not kept": "Uskladnenie obrazu OCI nebolo uchované", @@ -6308,13 +6311,13 @@ "The Proxmox inventory and the local configurations differ": "Inventár Proxmox nezodpovedá miestnym nastaveniam kontajnerov", "The ROCm image has no support for the AMD GPU of this host:": "Obraz ROCm nemá podporu pre AMD GPU tohto hostiteľa:", "The ROCm image of this application has no support for the AMD GPU of this host:": "Obraz ROCm tejto aplikácie nemá podporu pre AMD GPU tohto hostiteľa:", - "The Rclone configuration needs at least 1 GB": "Konfigurácia Rklonu potrebuje aspoň 1 GB", + "The Rclone configuration needs at least 1 GB": "Nastavenia Rclone vyžadujú aspoň 1 GB", "The Rclone rootfs needs at least 2 GB": "Rclone rootfs potrebuje aspoň 2 GB", - "The Selkies profile requires a verified LinuxServer image": "Profil Selkies requires a overený obraz LinuxServer", + "The Selkies profile requires a verified LinuxServer image": "Profil Selkies vyžaduje overený obraz LinuxServer", "The Tandoor files volume needs at least 2 GB": "Dátový zväzok súborov Tandoor musí mať najmenej 2 GB", "The URL does not contain the required parameters (id, pack, edition).": "URL neobsahuje potrebné parametre (id, pack, edition).", "The USB number can change after reconnecting or rebooting. This profile does not remap it automatically or handle Coral USB re-enumeration. Do not share a dongle already used by another service.": "Číslo USB sa môže zmeniť po opätovnom pripojení alebo reštarte. Tento profil nie je automaticky premapovať alebo spracovať Coral USB re-enumeration. Nezdieľajte šišku, ktorú už používa iná služba.", - "The Unifi-controller software is a powerful, enterprise wireless software engine ideal for high-density client deployments requiring low latency and high uptime performance.": "Softvér Unifi-controller je výkonný, podnikový bezdrôtový softvérový motor ideálny pre zavedenie klientov s vysokou hustotou requiring nízka latencia a vysoký výkon v čase.", + "The Unifi-controller software is a powerful, enterprise wireless software engine ideal for high-density client deployments requiring low latency and high uptime performance.": "UniFi Controller je výkonný softvér na správu podnikových bezdrôtových sietí s veľkým počtom klientov, nízkou latenciou a vysokou dostupnosťou.", "The VA-API device does not exist:": "Zariadenie VA-API neexistuje:", "The VM also has these audio devices assigned via PCI passthrough — typically added together with the GPU. Remove them too?": "VM má cez PCI passthrough priradené aj tieto audio zariadenia - zvyčajne sa pridávajú spolu s GPU. Odstrániť aj tie?", "The VM guest will have exclusive access to the GPU.": "Hosťovská VM bude mať výhradný prístup ku GPU.", @@ -6336,12 +6339,12 @@ "The added disk needs a whole-GiB size recorded by Proxmox": "Pridaný disk potrebuje veľkosť celého GiB zaznamenanú Proxmox", "The added disk size is not a whole GiB": "Pridaná veľkosť disku nie je celý GiB", "The additional path hides a system directory": "Ďalšia cesta skrýva systémový adresár", - "The address is already assigned on this host or cluster:": "Adresa je už priradená na tohto hostiteľa alebo klastra:", + "The address is already assigned on this host or cluster:": "Adresa je už priradená na tomto hostiteľovi alebo v klastri:", "The address must start with http:// or https://": "Adresa musí začať na http:// alebo https://", - "The administrator account, the public address and the UDP port are created on the first start, so the web interface opens directly on its login page.": "Správca účtu, verejná adresa a UDP port sú vytvorené na prvom začiatku, takže webové rozhranie sa otvorí priamo na prihlasovacej stránke.", - "The administrator email is not valid": "Správca e-mail nie je platný", + "The administrator account, the public address and the UDP port are created on the first start, so the web interface opens directly on its login page.": "Účet správcu, verejná adresa a port UDP sa vytvoria pri prvom spustení, takže webové rozhranie otvorí priamo prihlasovaciu stránku.", + "The administrator email is not valid": "E-mailová adresa správcu nie je platná", "The administrator user contains characters that are not allowed": "Užívateľ správcu obsahuje znaky, ktoré nie sú povolené", - "The all-in-one AI application.": "Aplikácia UI.", + "The all-in-one AI application.": "Univerzálna aplikácia umelej inteligencie.", "The application configuration needs a first start to complete.": "Konfigurácia aplikácie potrebuje prvý štart na dokončenie.", "The application could not be modified:": "Aplikáciu nebolo možné upraviť:", "The application could not be recovered:": "Aplikáciu sa nepodarilo znova zaregistrovať:", @@ -6376,7 +6379,7 @@ "The cached image is damaged; it will be downloaded again.": "Comment", "The changes need the application installer": "Zmeny vyžadujú inštalačný program aplikácie", "The character device does not exist:": "Znakové zariadenie neexistuje:", - "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Príkaz vyžaduje heslo, ktoré nie je ozvene. Po vytvorení užívateľov, webové rozhranie žiada o užívateľské meno a heslo.", + "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Príkaz sa opýta na heslo, ktoré sa pri písaní nezobrazuje. Po vytvorení používateľov bude webové rozhranie vyžadovať používateľské meno a heslo.", "The command does not name an image": "Príkaz neuvádza žiadny obraz", "The command reads its variables from a file; write them in the command or use a Compose file": "Príkaz číta premenné zo súboru; zadajte ich priamo v príkaze alebo použite súbor Compose", "The command runs the container as the user of the host; the container uses the user of its image instead.": "Príkaz požaduje spustenie kontajnera pod používateľom hostiteľa; kontajner namiesto toho použije používateľa svojho obrazu.", @@ -6402,7 +6405,7 @@ "The container disks do not match the saved record": "disky kontajnera nezodpovedajú uloženému záznamu", "The container does not exist:": "Nádoba neexistuje:", "The container does not have the fuse=1 feature enabled": "Kontajner nemá poistky = 1 zapnuté", - "The container does not need it any more; an update downloads the new version when there is one.": "Kontajner už nepotrebuje; aktualizácia si stiahne novú verziu, keď je.", + "The container does not need it any more; an update downloads the new version when there is one.": "Kontajner ho už nepotrebuje. Pri aktualizácii sa stiahne nový obraz, ak je dostupný.", "The container gets its own address and its own volumes, so the networks and volumes declared in the file are not used.": "Kontajner dostane vlastnú adresu a vlastné dátové úložiská, preto sa siete a úložiská definované v súbore nepoužijú.", "The container has advanced Proxmox settings outside the supported profile": "Kontajner má pokročilé nastavenia Proxmox mimo podporovaného profilu", "The container has an operation pending; finish or recover it first": "Kontajner má nedokončenú operáciu; najprv ju dokončite alebo obnovte", @@ -6425,9 +6428,9 @@ "The container stopped:": "Kontajner sa zastavil:", "The container takes its time zone from Proxmox, so the time files of the host are not attached to it.": "Kontajner preberá časové pásmo z Proxmoxu, preto sa doň nepripájajú súbory hostiteľa s nastavením času.", "The container was changed outside ProxMenux and an update would discard those changes:": "Kontajner bol zmenený mimo ProxMenux a aktualizácia by sa zbaviť týchto zmien:", - "The container was created from a downloaded OCI image": "Kontajner bol vytvorený zo stiahnutého obrázku OCI", + "The container was created from a downloaded OCI image": "Kontajner bol vytvorený zo stiahnutého obrazu OCI", "The containers do not need them any more; an update downloads the new versions when there are any.": "Kontajnery ich už nepotrebujú; aktualizácia stiahne nové verzie, ak nejaké existujú.", - "The containers were created from downloaded OCI images": "Kontajnery boli vytvorené zo stiahnutých obrázkov OCI", + "The containers were created from downloaded OCI images": "Kontajnery boli vytvorené zo stiahnutých obrazov OCI", "The coordinated backup was modified": "Koordinovaná záloha bola zmenená", "The credential file changed during this operation. Try again; it was not overwritten.": "Súbor s prihlasovacími údajmi sa počas tejto operácie zmenil. Skúste to znova; súbor nebol prepísaný.", "The current driver will be completely uninstalled before installing the new version. Continue?": "Aktuálny ovládač sa pred inštaláciou novej verzie úplne odinštaluje. Pokračovať?", @@ -6443,7 +6446,7 @@ "The dedicated adapter still requires replaying its rootfs changes": "Špecializovaný adaptér stále requires prehráva svoje rootfs zmeny", "The dependency hook and the stack recipe differ": "Spúšťací skript závislostí nezodpovedá konfigurácii aplikácie", "The dependency hook was modified; review it before updating": "Spúšťací skript závislostí bol zmenený; pred aktualizáciou ho skontrolujte", - "The developer-friendly cloud platform for building and running LLM agents for AI-native applications.": "Developer-friendly cloud platforma pre budovanie a prevádzku LLM agentov pre AI-natívne aplikácie.", + "The developer-friendly cloud platform for building and running LLM agents for AI-native applications.": "Cloudová platforma pre vývojárov na tvorbu a prevádzku agentov LLM v aplikáciách postavených na AI.", "The device directory does not exist:": "Adresár zariadenia neexistuje:", "The device must keep its /dev path inside the LXC:": "Zariadenie musí udržať svoju / Dev cestu vnútri LXC:", "The device must keep its native path without duplicates": "Zariadenie musí udržiavať svoju pôvodnú cestu bez duplikátov", @@ -6455,7 +6458,7 @@ "The disk size cannot be reproduced": "Veľkosť disku nie je možné reprodukovať", "The disk usage of the container could not be read": "Použitie disku kontajnera nebolo možné prečítať", "The disks of the failed attempt could not be removed and stay in the temporary container:": "Disky neúspešného pokusu sa nepodarilo odstrániť a zostávajú v dočasnom kontajneri:", - "The domain must resolve to the public address of this network before the certificate can be issued.": "Doména musí pred vydaním certifikátu vyriešiť verejnú adresu tejto siete.", + "The domain must resolve to the public address of this network before the certificate can be issued.": "Pred vydaním certifikátu musí doména smerovať na verejnú adresu tejto siete.", "The download client still needs to be configured.": "Stiahnuť klienta ešte treba nakonfigurovať.", "The download stopped progressing; cancelling this attempt.": "Sťahovanie zastavilo postup, zrušilo tento pokus.", "The downloaded image does not match its manifest": "Stiahnutý obrázok nezodpovedá jeho manifestu", @@ -6521,7 +6524,7 @@ "The image declares data paths that are still stored in the rootfs": "Obrázok deklaruje dátové cesty, ktoré sú stále uložené v rootfs", "The image did not grant the application user access to the devices; check its native init. Host permissions were not relaxed.": "Obrázok neumožňoval používateľovi aplikácie prístup k zariadeniam; skontrolujte jeho rodený init. Povolenie hostiteľa nebolo uvoľnené.", "The image did not pass the integrity check": "Obrázok neprešiel kontrolou integrity", - "The image documentation describes them:": "Dokumentácia k obrázku ich popisuje:", + "The image documentation describes them:": "Popis nájdete v dokumentácii obrazu:", "The image does not declare support for this architecture:": "Obraz nevyhlasuje podporu tejto architektúry:", "The image download did not complete correctly; downloading it again...": "Sťahovanie obrázku nebolo správne dokončené; sťahovanie...", "The image expects files that are given to it one by one:": "Obrázok očakáva súbory, ktoré sú mu dané jeden po druhom:", @@ -6532,17 +6535,17 @@ "The image manifest failed integrity verification. Try again or contact the image publisher.": "Overenie integrity manifestu obrazu zlyhalo. Skúste to znova alebo kontaktujte vydavateľa obrazu.", "The image or tag was not found on {registry}, or this account cannot access it. Check the reference.": "Obraz alebo tag sa v registri {registry} nenašiel alebo k nemu tento účet nemá prístup. Skontrolujte odkaz na obraz.", "The image requests NVIDIA, but the host has no working NVIDIA driver": "Obrázok požaduje NVIDIA, ale hostiteľ nemá žiadny funkčný ovládač NVIDIA", - "The image requests disabling part of the AppArmor or seccomp confinement. Continue only if you trust the image and accept this risk.": "Obraz si vyžaduje vypnutie časti AppArmor alebo seccomp väzenia. Pokračujte len vtedy, ak dôverujete obrazu a prijmete toto riziko.", - "The image requests disabling part of the AppArmor or seccomp confinement. The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. Continue only if you trust the image and accept this risk.": "Obraz si vyžaduje vypnutie časti AppArmor alebo seccomp väzenia. Skladba ponúka voliteľnú AppArmor alebo seccomp relaxáciu; zostane vypnutá, ak si ju užívateľ nevyberie. Pokračujte len vtedy, ak dôverujete obrazu a prijmete toto riziko.", + "The image requests disabling part of the AppArmor or seccomp confinement. Continue only if you trust the image and accept this risk.": "Obraz požaduje vypnutie časti obmedzení AppArmor alebo seccomp. Pokračujte iba vtedy, ak obrazu dôverujete a riziko prijímate.", + "The image requests disabling part of the AppArmor or seccomp confinement. The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. Continue only if you trust the image and accept this risk.": "Obraz požaduje vypnutie časti obmedzení AppArmor alebo seccomp. Súbor Compose ponúka ich voliteľné uvoľnenie, ktoré zostane vypnuté, kým ho používateľ nevyberie. Pokračujte iba vtedy, ak obrazu dôverujete a riziko prijímate.", "The image was not found in its registry, or it is private:": "Obrázok nebol nájdený v jeho registri, alebo je súkromný:", - "The imported Compose requests privileged, but the official jlesage/handbrake documentation does not require it; ProxMenux keeps the LXC unprivileged.": "Dovážané kompozície žiadosti privilegované, ale oficiálna jlesage/handbrake dokumentácia nie je require to; ProxMenux udržuje LXC neprivilegované.", + "The imported Compose requests privileged, but the official jlesage/handbrake documentation does not require it; ProxMenux keeps the LXC unprivileged.": "Importovaný súbor Compose požaduje privilegovaný režim, oficiálna dokumentácia jlesage/handbrake ho však nevyžaduje. ProxMenux ponechá kontajner LXC neprivilegovaný.", "The imported OCI groups are not numeric": "Dovezené skupiny OCI nie sú číselné", "The imported OCI user or group is not numeric": "Dovážaný užívateľ alebo skupina OCI nie je numerická", - "The initial user name and password stay written in /etc/pve/lxc/.conf as INIT_USERNAME and INIT_PASSWORD. They can be removed after the first login, with the container stopped.": "Počiatočné užívateľské meno a heslo zostávajú zapísané v /etc/pve/lxc/.conf ako INIT_USERNAME a INIT_PASSWORD. Môžu byť odstránené po prvom prihlásení, pričom kontajner sa zastaví.", - "The installation asks which one to use for these paths.": "Inštalácia žiada, ktorý z nich sa má použiť pre tieto cesty.", + "The initial user name and password stay written in /etc/pve/lxc/.conf as INIT_USERNAME and INIT_PASSWORD. They can be removed after the first login, with the container stopped.": "Počiatočné meno a heslo zostávajú v /etc/pve/lxc/.conf ako INIT_USERNAME a INIT_PASSWORD. Po prvom prihlásení ich možno odstrániť pri zastavenom kontajneri.", + "The installation asks which one to use for these paths.": "Inštalačný sprievodca sa opýta, ktorú možnosť použiť pre tieto cesty.", "The installation ended with exit code": "Inštalácia skončila kódom výstupu", "The installation requires a server restart to apply changes. Do you want to restart now?": "Na použitie zmien je po inštalácii potrebný reštart servera. Chcete reštartovať teraz?", - "The installation runs on the Proxmox node itself, as root": "Inštalácia beží na Proxmox uzol sám, ako koreň", + "The installation runs on the Proxmox node itself, as root": "Inštalácia beží priamo na uzle Proxmox pod používateľom root", "The installation stopped because of an unexpected error": "Inštalácia zastavená kvôli neočakávanej chybe", "The installation/changes require a server restart to apply correctly. Do you want to reboot now?": "Aby sa inštalácia alebo zmeny správne použili, server treba reštartovať. Chcete reštartovať teraz?", "The installer must run as root on Proxmox VE": "Inštalatér musí spustiť ako koreň na Proxmox VE", @@ -6550,7 +6553,7 @@ "The instance changed while it was being edited; open Modify again": "Inštancia sa zmenila počas úpravy; znova otvorte Upraviť", "The instance does not use NVIDIA": "Tento prípad nepoužíva NVIDIU", "The instance has a pending operation": "Inštancia má prebiehajúci operation", - "The instance identity or status must be reviewed before updating.": "Pred aktualizáciou sa musí preskúmať totožnosť alebo status prípadu.", + "The instance identity or status must be reviewed before updating.": "Pred aktualizáciou treba preveriť identitu alebo stav inštancie.", "The instance is not ready to be updated": "Prípad nie je pripravený na aktualizáciu", "The instance is not ready; review its pending operation": "Inštancia nie je pripravená; preskúma svoju očakávanú operation", "The instance record operation did not complete; no container was modified.": "Záznam o inštancii operation sa nedokončil; žiadna nádoba nebola zmenená.", @@ -6591,7 +6594,7 @@ "The new image could not be installed:": "Nový obrázok sa nepodarilo nainštalovať:", "The new image does not fit on the storage where images are downloaded:": "Nový image sa nezmestí na úložisko, kam sa sťahujú image:", "The new image does not keep a required executable": "Nový obrázok neudržuje spustiteľný required", - "The new image is more recent than the minimum age set for scheduled updates; it is not installed yet.": "Nový obrázok je novší ako minimálny vek nastavený pre plánované aktualizácie;ešte nie je nainštalovaný.", + "The new image is more recent than the minimum age set for scheduled updates; it is not installed yet.": "Nový obraz zatiaľ nedosiahol minimálny vek nastavený pre plánované aktualizácie, preto sa ešte nenainštaluje.", "The new image requires additional persistent paths": "Nový obraz vyžaduje ďalšie trvalé dátové adresáre", "The new image requires additional persistent paths; use Modify": "Nový obraz vyžaduje ďalšie trvalé cesty; použite Upraviť", "The new image requires additional persistent paths; use Recreate": "Nový obrázok requires ďalšie pretrvávajúce cesty; používať Recreate", @@ -6629,7 +6632,7 @@ "The persistent NVIDIA hook does not match the installer:": "Trvalý NVIDIA hák nezodpovedá inštalátorovi:", "The persistent WebUI credentials were not found": "Trvalé WebUI credentials neboli nájdené", "The physical NVIDIA selection changed": "Fyzický výber NVIDIA sa zmenil", - "The post-start configuration cannot be applied with the LXC stopped": "Konfigurácia po štarte sa nedá použiť s zastavením LXC.", + "The post-start configuration cannot be applied with the LXC stopped": "Nastavenia po spustení nemožno použiť, keď je kontajner LXC zastavený", "The postgres user was not found in the image": "Užívateľ postgres nebol nájdený v obrázku", "The prepared directory escapes the rootfs:": "Pripravený adresár unikne koreňom:", "The preselected VMID does not exist on this host:": "Predvybrané VMID na tomto hostovi neexistuje:", @@ -6680,7 +6683,7 @@ "The remote does not exist; create and authorize it first in the WebUI:": "Vzdialenosť neexistuje; vytvoriť a schváliť ju ako prvý na WebUI:", "The remote installer must run as root on Proxmox VE": "Diaľkový inštalátor musí bežať ako koreň na Proxmox VE", "The remote must already be created and authorized in the Rclone web UI. This operation restarts the CT and publishes two FUSE views on the host.": "Vzdialený musí byť už vytvorený a schválený v Rclone web UI. Tento operation reštartuje CT a zverejňuje dva pohľady FUSE na hostiteľa.", - "The remote path must be relative and cannot contain line breaks": "Diaľková dráha musí byť relatívna a nesmie obsahovať prestávky na priamke", + "The remote path must be relative and cannot contain line breaks": "Cesta vo vzdialenom úložisku musí byť relatívna a nesmie obsahovať zalomenia riadkov", "The removal could not be prepared:": "Odstránenie nebolo možné pripraviť:", "The render device does not belong to the GPU of that choice": "Renderovacie zariadenie nepatrí ku GPU pre zvolený profil", "The render device does not exist:": "Renderovacie zariadenie neexistuje:", @@ -6690,13 +6693,13 @@ "The resource edit could not be fully rolled back. Open Manage again to review and import the current Proxmox settings.": "Zmenu prostriedkov sa nepodarilo úplne vrátiť späť. Znova otvorte správu aplikácií, skontrolujte aktuálne nastavenia systému Proxmox a prevezmite ich.", "The restored service did not pass its health check": "Obnovená služba neprešla kontrolou stavu", "The restored service stopped; the recovery is not confirmed": "Obnovená služba sa zastavila; obnova sa nepotvrdila", - "The reviewed Tandoor stack does not require a privileged LXC.": "Preskúmaný Tandoor stack require privilegovaný LXC.", + "The reviewed Tandoor stack does not require a privileged LXC.": "Overená zostava Tandoor nevyžaduje privilegovaný kontajner LXC.", "The root password is asked for when connecting from another machine. Inside the container, the mariadb command opens as root without a password.": "Heslo používateľa root sa vyžaduje pri pripojení z iného počítača. Vnútri kontajnera sa príkaz mariadb otvorí ako root bez hesla.", "The rootfs capture only belongs to the running installation": "Rootfs capture patrí len do bežiacej inštalácie", "The rootfs is not managed by Proxmox": "Rootfs nie je riadený Proxmox", "The rootfs is not mounted": "Korene nie sú namontované", "The same GPU cannot be used by two VMs at the same time.": "Rovnakú GPU nemôžu používať dve VM naraz.", - "The same connection can be given as container variables instead of the file: UN_SONARR_0_URL and UN_SONARR_0_API_KEY, or the UN_RADARR_0_ equivalents.": "Rovnaké spojenie možno uviesť ako kontajnerové premenné namiesto súboru: UN_SONARR_0_URL a UN_SONARR_0_API_KEY, alebo UN RADARR 0 equivalents.", + "The same connection can be given as container variables instead of the file: UN_SONARR_0_URL and UN_SONARR_0_API_KEY, or the UN_RADARR_0_ equivalents.": "Namiesto súboru možno pripojenie nastaviť premennými kontajnera: UN_SONARR_0_URL a UN_SONARR_0_API_KEY alebo zodpovedajúcimi premennými UN_RADARR_0_.", "The saved MOTD state is invalid; no changes were made": "Uložený stav MOTD je neplatný;neboli vykonané žiadne zmeny", "The saved OCI record is incomplete or has an unexpected format.": "Uložený záznam OCI je neúplný alebo má neočakávaný formát.", "The saved image channel is checked for a newer image. If replacement is needed, the CT is stopped and a native backup is verified before its root is replaced. Host directories are outside that backup.": "Kanál uložených obrázkov sa skontroluje, či neobsahuje novší obrázok. Ak je potrebná výmena, CT sa zastaví a pred nahradením koreňa sa overí natívna záloha. Hostiteľské adresáre sú mimo tejto zálohy.", @@ -6762,8 +6765,8 @@ "The shared directory points to a protected host path": "Zdieľaný adresár ukazuje na chránenú cestu hostiteľa", "The shared path exists but is not a directory:": "Spoločná cesta existuje, ale nie je adresárom:", "The size of existing disks is not rounded": "Veľkosť existujúcich diskov nie je zaokrúhlená", - "The source Compose requests privileged: true, but this does not prove that the image needs a privileged LXC. ProxMenux will use an unprivileged LXC by default and will offer the broad mode only as an option. Continue only if you trust the image and accept this risk.": "Zdroj tvoria požiadavky privilegované: pravda, ale to nedokazuje, že obraz potrebuje privilegovaný LXC. ProxMenux štandardne použije neprivilegovaný LXC a ponúkne široký režim len ako možnosť. Pokračujte len vtedy, ak dôverujete obrazu a prijmete toto riziko.", - "The source Compose requests privileged: true, but this does not prove that the image needs a privileged LXC. ProxMenux will use an unprivileged LXC by default and will offer the broad mode only as an option. The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. The Docker rootlesskit profile does not exist in LXC and will be replaced by AppArmor unconfined, which is less restrictive. Continue only if you trust the image and accept this risk.": "Zdroj tvoria požiadavky privilegované: pravda, ale to nedokazuje, že obraz potrebuje privilegovaný LXC. ProxMenux štandardne použije neprivilegovaný LXC a ponúkne široký režim len ako možnosť. Skladba ponúka voliteľnú AppArmor alebo seccomp relaxáciu; zostane vypnutá, ak si ju užívateľ nevyberie. Profil Docker rootlesskit neexistuje v LXC a nahradí ho AppArmor unconfined, čo je menej obmedzujúce. Pokračujte len vtedy, ak dôverujete obrazu a prijmete toto riziko.", + "The source Compose requests privileged: true, but this does not prove that the image needs a privileged LXC. ProxMenux will use an unprivileged LXC by default and will offer the broad mode only as an option. Continue only if you trust the image and accept this risk.": "Zdrojový súbor Compose požaduje privileged: true, čo však nedokazuje, že obraz vyžaduje privilegovaný kontajner LXC. ProxMenux predvolene použije neprivilegovaný kontajner LXC a režim so širšími oprávneniami ponúkne iba ako možnosť. Pokračujte iba vtedy, ak obrazu dôverujete a toto riziko prijímate.", + "The source Compose requests privileged: true, but this does not prove that the image needs a privileged LXC. ProxMenux will use an unprivileged LXC by default and will offer the broad mode only as an option. The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. The Docker rootlesskit profile does not exist in LXC and will be replaced by AppArmor unconfined, which is less restrictive. Continue only if you trust the image and accept this risk.": "Zdrojový súbor Compose požaduje privileged: true, čo však nedokazuje, že obraz vyžaduje privilegovaný kontajner LXC. ProxMenux predvolene použije neprivilegovaný kontajner a širšie oprávnenia ponúkne iba voliteľne. Voliteľné uvoľnenie AppArmor alebo seccomp zostane vypnuté, kým ho používateľ nevyberie. Profil Docker rootlesskit v LXC neexistuje a nahradí sa menej obmedzujúcim režimom AppArmor unconfined. Pokračujte iba vtedy, ak obrazu dôverujete a riziko prijímate.", "The source VM also has these audio devices, likely added together with the GPU. Remove them too?": "Zdrojová VM má aj tieto audio zariadenia, pravdepodobne pridané spolu s GPU. Odstrániť aj tie?", "The specified directory does not exist:": "Zadaný priečinok neexistuje:", "The stability period must be shorter than the healthcheck timeout": "Obdobie stability musí byť kratšie ako časový limit pre kontrolu zdravotného stavu.", @@ -6771,7 +6774,7 @@ "The stack does not have the expected native hook": "Aplikácia nemá očakávaný natívny spúšťací skript", "The stack journal is outside the registry": "Záznam operácie celej aplikácie je mimo registra", "The stack member has no declared adaptation profile": "Stack člen nemá deklarovaný adaptačný profil", - "The stack name only accepts lowercase letters, numbers and hyphens": "Názov stohu prijíma len malé písmená, čísla a pomlčky", + "The stack name only accepts lowercase letters, numbers and hyphens": "Názov zostavy môže obsahovať iba malé písmená, číslice a pomlčky", "The stack needs member adaptations or a verification of missing volumes": "Aplikácia vyžaduje úpravy svojich kontajnerov alebo overenie chýbajúcich dátových zväzkov", "The stack operation had already finished": "Operácia celej aplikácie už bola dokončená", "The stack operation has not finished yet": "Operácia celej aplikácie ešte nebola dokončená", @@ -6786,7 +6789,7 @@ "The storage chosen for this backup has no room either:": "Ani na úložisku vybratom pre túto zálohu nie je dostatok miesta:", "The storage does not accept backups:": "Úložisko neprijíma zálohy:", "The storage has been removed and the disk unmounted.": "Úložisko bolo odstránené a disk odpojený.", - "The storage has less than 40 GB free for the ROCm image.": "V úložisku je voľných menej ako 40 GB pre obrázok ROCm.", + "The storage has less than 40 GB free for the ROCm image.": "Na úložisku je menej než 40 GB voľného miesta pre obraz ROCm.", "The sysctl content was modified outside the saved record": "Obsah sysctl bol upravený mimo uloženého záznamu", "The sysctl include is a link:": "Sysctl patrí je odkaz:", "The sysctl include is not a safe host file": "Sysctl patrí nie je bezpečný hostiteľský súbor", @@ -6810,9 +6813,9 @@ "The web UI user contains characters that are not allowed": "Webový užívateľ UI obsahuje znaky, ktoré nie sú povolené", "The web interface is served over plain HTTP on port 51821 (INSECURE=true). Keep it inside the local network or publish it through a reverse proxy with TLS.": "Webové rozhranie sa podáva cez obyčajný HTTP na porte 51821 (INSECURE=true). Držte ho v miestnej sieti alebo ho publikujte prostredníctvom reverzného proxy s TLS.", "The web interface uses a self-signed certificate, so the browser shows a warning the first time.": "Webové rozhranie používa samopodpísaný certifikát, takže prehliadač prvýkrát zobrazí varovanie.", - "The wizard writes a .conf file in /config. Restart the container afterwards so the bot starts with that configuration.": "Čarodejník píše súbor .conf v /config. Potom znovu spustite kontajner, takže robot začne s touto konfiguráciou.", + "The wizard writes a .conf file in /config. Restart the container afterwards so the bot starts with that configuration.": "Sprievodca zapíše súbor .conf do /config. Potom kontajner reštartujte, aby robot použil tieto nastavenia.", "The world's fastest framework for building websites": "Najrýchlejší rámec na svete pre vytváranie webových stránok", - "Thelounge (a fork of shoutIRC) is a web IRC client that you host on your own server.": "Thelounge (fork screamIRC) je webový IRC klient, ktorý hosťujete na svojom vlastnom serveri.", + "Thelounge (a fork of shoutIRC) is a web IRC client that you host on your own server.": "The Lounge (odnož shoutIRC) je webový IRC klient, ktorý prevádzkujete na vlastnom serveri.", "Then bind-mount to container": "Potom pripojiť do kontajnera cez bind mount", "Then change the VM display to none (vga: none) when the guest is stable.": "Keď bude VM stabilná, zmeňte jej display na none (vga: none).", "Then change the VM display to none (vga: none) when the system is stable.": "Keď bude systém stabilný, zmeňte display VM na none (vga: none).", @@ -6836,7 +6839,7 @@ "These paths will not be restored live and will be extracted for manual recovery.": "Tieto cesty sa neobnovia za behu a rozbalia sa na ručnú obnovu.", "These settings require an application-specific adaptation. The image was not downloaded and the container was not recreated.": "Tieto nastavenia vyžadujú úpravu špecifickú pre aplikáciu. Obraz sa nestiahol a kontajner sa nevytvoril znova.", "These values are asked during the installation:": "Tieto hodnoty sa požadujú počas inštalácie:", - "They are described in the image documentation:": "Ich popis nájdete v dokumentácii image:", + "They are described in the image documentation:": "Ich popis nájdete v dokumentácii obrazu:", "They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Pred pokračovaním budú pridané do oci-compose. Nepodporované alebo zmenené zdroje sa neimportujú.", "They will be saved in oci-compose before continuing.": "Pred pokračovaním budú uložené v oci-compose.", "This CIFS share is mounted with restrictive permissions.": "Toto CIFS zdieľanie je pripojené s obmedzujúcimi oprávneniami.", @@ -6880,7 +6883,7 @@ "This host has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PVE source will be disabled. Enterprise Ceph sources, if present, will be disabled without choosing a replacement Ceph channel; configure Ceph separately if needed.": "Tento hostiteľ nemá žiadne aktívne predplatné, prepnúť na úložisko bez predplatného? Neprístupný zdroj Enterprise PVE bude zakázaný. Enterprise Ceph zdroje, ak sú prítomné, budú deaktivované bez výberu náhradného Ceph kanála; v prípade potreby nakonfigurujte Ceph samostatne.", "This image cannot be installed as it is described:": "Tento obraz nemožno nainštalovať podľa zadanej konfigurácie:", "This image has no compatible Linux image for this host ({architecture}).": "Tento obraz nemá variant Linuxu kompatibilný s týmto hostiteľom ({architecture}).", - "This image requires the host module": "Tento obrázok requires hostiteľský modul", + "This image requires the host module": "Tento obraz vyžaduje modul hostiteľa", "This import rejects these paths without checking whether they are files or directories.": "Tento import odmieta tieto cesty bez kontroly, či ide o súbory alebo adresáre.", "This installation will:": "Táto inštalácia urobí toto:", "This installer will:": "Tento inštalátor urobí toto:", @@ -6892,7 +6895,7 @@ "This is an external community script maintained by": "Toto je externý komunitný skript, ktorý spravuje", "This is an external script that creates a macOS VM in Proxmox VE in just a few steps, whether you are using AMD or Intel hardware.": "Toto je externý skript, ktorý v Proxmox VE vytvorí VM s macOS v niekoľkých krokoch. Funguje na AMD aj Intel hardvéri.", "This is not a coordinated stack": "Toto nie je aplikácia so spoločne spravovanými kontajnermi", - "This is not a valid image reference:": "Toto nie je platný odkaz na obrázok:", + "This is not a valid image reference:": "Toto nie je platný odkaz na obraz:", "This is unexpected since credentials were validated.": "Toto je nečakané, pretože prihlasovacie údaje už boli overené.", "This is what ProxMenux understood from the": "To je to, čo ProxMenux pochopil z", "This is what the oci-compose installs:": "Toto nainštaluje oci-compose:", @@ -6906,7 +6909,7 @@ "This means the credentials are incorrect.": "To znamená, že prihlasovacie údaje nie sú správne.", "This menu requires Proxmox Backup Server.": "Táto ponuka vyžaduje Proxmox Backup Server.", "This might indicate network connectivity issues.": "Môže to znamenať problém so sieťovým pripojením.", - "This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Tento monitor používa privilegovaný LXC, zdieľa procesy a sieť s Proxmox a vypne AppArmor v CT. Používa IP adresu a firewall hostiteľa. Kompromitovaný obraz by mohol mať vplyv na hostiteľa; nevystavujte svoje webové UI internetu.", + "This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Tento monitor používa privilegovaný kontajner LXC, zdieľa procesy a sieť s Proxmoxom a vypína AppArmor v kontajneri. Používa IP adresu a firewall hostiteľa. Napadnutý obraz by mohol ohroziť hostiteľa. Jeho webové rozhranie nevystavujte na internet.", "This only removes the schedule. Existing backup archives are NOT deleted.": "Týmto sa odstráni iba plán. Existujúce zálohy NEBUDÚ vymazané.", "This operation may take several minutes and requires internet connectivity.": "Táto operácia môže trvať niekoľko minút a vyžaduje pripojenie na internet.", "This operation requires a Proxmox VE host. Use the PBS menu on Proxmox Backup Server.": "Táto operácia vyžaduje hostiteľa Proxmox VE. Na Proxmox Backup Serveri použite ponuku PBS.", @@ -6937,7 +6940,7 @@ "This stack requires replaying specific rootfs adaptations. Coordinated updates are not yet enabled for it.": "Tento zásobník requires prehráva špecifické korektúry. Koordinované aktualizácie pre ňu zatiaľ nie sú povolené.", "This state has a high probability of VM startup/reset failures.": "Tento stav má vysokú pravdepodobnosť zlyhania štartu alebo resetu VM.", "This state indicates a high risk of passthrough failure due to": "Tento stav znamená vysoké riziko zlyhania passthrough pre", - "This template requests the host PID namespace, which has no validated safe LXC translation yet": "Táto šablóna vyžaduje PID formát hostiteľa, ktorý zatiaľ nemá overený bezpečný LXC preklad", + "This template requests the host PID namespace, which has no validated safe LXC translation yet": "Táto šablóna požaduje menný priestor PID hostiteľa, ktorý zatiaľ nemá overený bezpečný prevod do LXC", "This tool is designed for systems with AMD GPUs.": "Tento nástroj je určený pre systémy s AMD GPU.", "This tool is designed for systems with Intel GPUs.": "Tento nástroj je určený pre systémy s Intel GPU.", "This translator only supports the Nextcloud stack": "Tento prekladateľ podporuje iba Nextcloud stack", @@ -6964,7 +6967,7 @@ "This will restart the network service and may cause a brief disconnection. Continue?": "Reštartuje sa sieťová služba a môže dôjsť ku krátkemu odpojeniu. Pokračovať?", "This will take time. Answer prompts carefully - see notes below.": "Bude to chvíľu trvať. Na otázky odpovedajte opatrne - pozrite poznámky nižšie.", "This will upgrade this node to Proxmox VE 9 on Debian Trixie.": "Týmto sa tento uzol aktualizuje na Proxmox VE 9 na Debiane Trixie.", - "Thunderbird is a free and open-source personal information manager primarily used as an e-mail client with a calendar and contactbook, as well as an RSS feed reader, chat client, and news client.": "Thunderbird je bezplatný a open-source personal information manager používaný predovšetkým ako e-mailový klient s kalendárom a kontaktnou knihou, rovnako ako RSS feed čítačka, chat klient, a novinky klient.", + "Thunderbird is a free and open-source personal information manager primarily used as an e-mail client with a calendar and contactbook, as well as an RSS feed reader, chat client, and news client.": "Thunderbird je bezplatný správca osobných informácií s otvoreným zdrojovým kódom. Slúži najmä ako e-mailový klient s kalendárom a adresárom kontaktov, ale aj ako čítačka RSS, chatový klient a klient diskusných skupín.", "Tick the paths to include in this backup. Press \"Add custom path\" to add a folder or file of your own to the list.": "Označte cesty, ktoré chcete zahrnúť do tejto zálohy. Stlačte \"Pridať vlastnú cestu\", ak chcete do zoznamu pridať vlastný priečinok alebo súbor.", "Tick the paths to remove (they will not be deleted from disk — only from this list):": "Označte cesty na odstránenie (z disku sa nevymažú - odstránia sa iba z tohto zoznamu):", "Time is up; Home Assistant OS could not be confirmed as running": "Čas vypršal; Home Assistant OS nebolo možné potvrdiť ako bežiace", @@ -7004,15 +7007,15 @@ "Tool output:": "Výstup nástroja:", "Tools": "Nástroje", "Top memory processes in CT": "Procesy s najväčšou spotrebou pamäte v CT", - "Top-level configs, secrets and other global options are not yet supported": "Najlepšie konfigy, tajomstvá a iné globálne možnosti zatiaľ nie sú podporované", - "Top-level volume options are not yet supported": "Najvyššie možnosti objemu zatiaľ nie sú podporované", + "Top-level configs, secrets and other global options are not yet supported": "Položky configs, secrets a ďalšie globálne voľby na najvyššej úrovni zatiaľ nie sú podporované", + "Top-level volume options are not yet supported": "Voľby dátových zväzkov na najvyššej úrovni zatiaľ nie sú podporované", "Total": "Spolu", "Total members:": "Počet členov:", "Total routes": "Počet trás", "Total size:": "Celková veľkosť:", "Transaction log:": "Záznam transakcií:", "Translation files:": "Prekladové súbory:", - "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission je určený pre jednoduché, výkonné použitie. Transmission má funkcie, ktoré chcete od BitTorrent klienta: šifrovanie, webové rozhranie, peer exchange, magnet odkazy, DHT, μTP, UPnP a NAT-PMP port forwarding, podpora webových seedov, sledovať adresáre, tracker editovanie, globálne a per-torrent rýchlostné limity, a ďalšie.", + "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission je ľahko ovládateľný klient BitTorrent s bohatými funkciami: šifrovaním, webovým rozhraním, výmenou partnerov, magnetickými odkazmi, DHT, µTP, presmerovaním portov cez UPnP a NAT-PMP, webovými zdrojmi, sledovaním adresárov, úpravou trackerov a spoločnými aj samostatnými limitmi rýchlosti torrentov.", "Tried pvesm path and manual detection methods": "Skúšal sa pvesm path aj ručné hľadanie", "Trust this certificate and save it for scheduled backups?": "Dôverovať tomuto certifikátu a uložiť ho pre naplánované zálohy?", "Try Again": "Skúsiť znova", @@ -7022,7 +7025,7 @@ "Try automatic repair of detected issues": "Skúsiť automatickú opravu zistených problémov", "Try username and password authentication.": "Vyskúšajte overenie používateľského mena a hesla.", "Tvheadend works as a proxy server: is a TV streaming server and recorder for Linux, FreeBSD and Android supporting DVB-S, DVB-S2, DVB-C, DVB-T, ATSC, ISDB-T, IPTV, SAT>IP and HDHomeRun as input sources.": "Tvheadend funguje ako proxy server: je televízny streamovací server a záznamník pre Linux, FreeBSD a Android podporujúci DVB-S, DVB-S2, DVB-C, DVB-T, ATSC, ISDB-T, IPTV, SAT>IP a HDHomeRun ako vstupné zdroje.", - "Twingate Connector for self-hosted server": "Twingate Connector pre self-hosted server", + "Twingate Connector for self-hosted server": "Twingate Connector pre vlastný server", "Two-factor authentication and backup codes will be removed.": "Dvojfaktorové overenie a záložné kódy sa odstránia.", "Type": "Typ", "Type the device path EXACTLY to confirm formatting:": "Na potvrdenie formátovania zadajte PRESNÚ cestu zariadenia:", @@ -7032,7 +7035,7 @@ "Typed value does not match selected disk. Operation cancelled.": "Zadaná hodnota sa nezhoduje s vybraným diskom. Operácia bola zrušená.", "UID in CT": "UID v CT", "UID of the plex user (also owner of the GPU device)": "UID používateľa plex (tiež majiteľ zariadenia GPU)", - "UID that Emby runs as": "UID, že Emby beží ako", + "UID that Emby runs as": "UID používateľa, pod ktorým beží Emby", "UPGRADE PROMPTS - RECOMMENDED ANSWERS:": "OTÁZKY PRI AKTUALIZÁCII - ODPORÚČANÉ ODPOVEDE:", "UPS monitoring and power outage notification system": "Systém monitorovania UPS a oznamovania výpadku napájania", "USB Accelerators:": "USB akcelerátory:", @@ -7050,7 +7053,7 @@ "Udev rules for Coral USB devices added and rules reloaded.": "Udev pravidlá pre Coral USB zariadenia boli pridané a znovu načítané.", "Udev rules for Coral USB devices already exist.": "Udev pravidlá pre Coral USB zariadenia už existujú.", "Udev rules for Coral USB devices appended and rules reloaded.": "Udev pravidlá pre Coral USB zariadenia boli doplnené a znovu načítané.", - "UltiMaker Cura is free, easy-to-use 3D printing software trusted by millions of users. Fine-tune your 3D model with 400+ settings for the best slicing and printing results.": "UltiMaker Cura je bezplatný, ľahko použiteľný 3D tlačový softvér dôveryhodný miliónom užívateľov. Vyrovnajte svoj 3D model s nastavením 400+ pre najlepšie výsledky krájania a tlače.", + "UltiMaker Cura is free, easy-to-use 3D printing software trusted by millions of users. Fine-tune your 3D model with 400+ settings for the best slicing and printing results.": "UltiMaker Cura je bezplatný a ľahko použiteľný softvér na 3D tlač, ktorému dôverujú milióny používateľov. Viac ako 400 nastavení umožňuje doladiť prípravu vrstiev modelu a výsledok tlače.", "Umbrel OS installer script by Helper Scripts\n\nVisit the GitHub repo to learn more, contribute, or support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm": "Inštalačný skript Umbrel OS od Helper Scripts\n\nNavštívte GitHub repozitár, kde nájdete viac informácií, môžete prispieť alebo podporiť projekt:\n\nhttps://community-scripts.github.io/ProxmoxVE/scripts?id=umbrel-os-vm", "Unable to detect Proxmox version": "Nepodarilo sa zistiť verziu Proxmoxu", "Unable to detect Proxmox version.": "Nepodarilo sa zistiť verziu Proxmoxu.", @@ -7062,8 +7065,8 @@ "Understand the security implications of privileged containers": "Uistite sa, že rozumiete bezpečnostným dôsledkom privilegovaných kontajnerov", "Unexpected Proxmox inventory; recovery blocked": "Neočakávaný inventár Proxmox; zablokovaná obnova", "Unexpected formatting directory in the new Valkey volume": "Neočakávaný formátovanie adresára v novom hlasitosti Valkey", - "Ungoogled Chromium is Google Chromium, sans dependency on Google web services.": "Ungoogled Chromium je Google Chromium, sans závislosti na webových služieb Google.", - "Unified LLM Fine-Tuning with 100+ Models": "Unified LLM Fine-Tuning s 100+ Models", + "Ungoogled Chromium is Google Chromium, sans dependency on Google web services.": "Ungoogled Chromium je Chromium bez závislosti od webových služieb Google.", + "Unified LLM Fine-Tuning with 100+ Models": "Jednotné dolaďovanie viac ako 100 jazykových modelov LLM", "Uninstall Coral drivers and configuration": "Odinštalovať ovládače a nastavenie Coral", "Uninstall Fail2Ban": "Odinštalovať Fail2Ban", "Uninstall Lynis": "Odinštalovať Lynis", @@ -7094,7 +7097,7 @@ "Unknown CPU vendor. Cannot determine IOMMU parameter.": "Neznámy výrobca CPU. Parameter IOMMU sa nedá určiť.", "Unknown GPU": "Neznáma GPU", "Unknown adapter role": "Neznáma úloha adaptéra", - "Unknown credential service:": "Neznámy credential servis:", + "Unknown credential service:": "Neznáma služba pre prihlasovací údaj:", "Unknown dependency:": "Neznáma závislosť:", "Unknown host monitor": "Neznámy monitor hostiteľa", "Unknown model": "Neznámy model", @@ -7113,8 +7116,8 @@ "Unmounting": "Odpájam", "Unmounting disk...": "Odpájam disk...", "Unpackerr configured": "Konfigurovaný Unpackerr", - "Unpackerr has no web interface and extracts nothing until it is pointed at a Starr application. Uncomment the [sonarr.0] or [radarr.0] section in /config/unpackerr.conf inside the container, set its url and api_key, then restart the container.": "Unpackerr nemá webové rozhranie a nič nevyberá, kým nie je namierená na Starr aplikáciu. Odpojte časť [sonarr.0] alebo [radarr.0] v časti /config/unpackerr.conf vo vnútri kontajnera, nastavte jeho url a api key, potom znovu reštartujte kontajner.", - "Unpackerr requires Sonarr, Radarr or Lidarr in this suite": "Unpackerr requires Sonarr, Radarr alebo Lidarr v tomto súbore", + "Unpackerr has no web interface and extracts nothing until it is pointed at a Starr application. Uncomment the [sonarr.0] or [radarr.0] section in /config/unpackerr.conf inside the container, set its url and api_key, then restart the container.": "Unpackerr nemá webové rozhranie a nerozbaľuje nič, kým ho neprepojíte s aplikáciou Starr. V /config/unpackerr.conf v kontajneri odkomentujte sekciu [sonarr.0] alebo [radarr.0], nastavte url a api_key a kontajner reštartujte.", + "Unpackerr requires Sonarr, Radarr or Lidarr in this suite": "Unpackerr v tejto zostave vyžaduje Sonarr, Radarr alebo Lidarr", "Unpackerr stopped during its first start": "Unpackerr zastavil počas svojho prvého štartu", "Unprivileged": "Neprivilegovaný", "Unprivileged Container Access": "Prístup neprivilegovaného kontajnera", @@ -7134,7 +7137,7 @@ "Unrecognized stack adapter or role": "Nerozpoznaný stack adaptér alebo role", "Unrecognized stack adapter:": "Nerozpoznaný adaptér aplikácie s viacerými kontajnermi:", "Unrecognized stack structure:": "Nerozpoznaná štruktúra aplikácie s viacerými kontajnermi:", - "Unrecognized volume definition": "Nerozpoznaná definícia objemu", + "Unrecognized volume definition": "Nerozpoznaná definícia dátového zväzku", "Unresolved variable:": "Nevyriešená premenná:", "Unsafe OCI archive path": "Nebezpečná cesta k archívu OCI", "Unsafe dependency contract": "Nebezpečné nastavenie závislostí", @@ -7183,10 +7186,10 @@ "Unsupported pre-start check:": "Nepodporovaná kontrola pred začiatkom:", "Unsupported pre-start repair:": "Nepodporovaná oprava pred štartom:", "Unsupported prlimit resource": "Nepodporovaný prlimit zdroj", - "Unsupported secret generator:": "Nepodporovaný tajný generátor:", + "Unsupported secret generator:": "Nepodporovaný generátor tajných údajov:", "Unsupported storage mode:": "Nepodporovaný režim skladovania:", "Unsupported tmpfs options": "Nepodporované možnosti tmpfs", - "Unsupported volume options:": "Nepodporované možnosti objemu:", + "Unsupported volume options:": "Nepodporované voľby dátového zväzku:", "Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host and changes nothing otherwise.": "Kým nie sú znova zaregistrované, nemožno ich aktualizovať ani spravovať odtiaľto. Obnova najprv skontroluje, či všetko, čo potrebujú, je na tomto hostiteľovi a inak nič nezmení.", "Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host; an application that lacks something is not registered.": "Kým nie sú znova zaregistrované, nemožno ich aktualizovať ani spravovať odtiaľto. Obnova najprv skontroluje, či všetko, čo potrebujú, je na tomto hostiteľovi; aplikácia, ktorej niečo chýba, nie je zaregistrovaná.", "Until they are registered again they cannot be updated or managed from here. The recovery checks first what prevents their registration; an application with such a problem is not registered. A Rclone mount that has to be enabled again does not prevent it, but then the applications are not started automatically.": "Kým nie sú znova zaregistrované, nemožno ich odtiaľto aktualizovať ani spravovať. Pred opätovnou registráciou sa skontroluje, čo jej bráni. Aplikácia s takýmto problémom sa nezaregistruje. Pripojenie Rclone, ktoré treba znova povoliť, registrácii nebráni, ale aplikácie sa potom nespúšťajú automaticky.", @@ -7221,7 +7224,7 @@ "Update prepared": "Aktualizácia", "Update selected OCI applications": "Aktualizovať vybrané aplikácie OCI", "Update system to latest PVE 8.4+ (if not done already):": "Aktualizujte systém na najnovšie PVE 8.4+ (ak to ešte nie je hotové):", - "Update the image with the saved configuration": "Aktualizovať obrázok pomocou uloženej konfigurácie", + "Update the image with the saved configuration": "Aktualizovať obraz s uloženými nastaveniami", "Update the whole stack?": "Aktualizovať celú aplikáciu?", "Updated": "Aktualizované", "Updated sharedfiles group to GID: 101000": "Skupina sharedfiles bola aktualizovaná na GID: 101000", @@ -7317,13 +7320,13 @@ "User chose to exit for manual backup creation.": "Používateľ ukončil akciu, aby mohol ručne vytvoriť zálohu.", "User name for the SSH login": "Užívateľské meno pre prihlásenie SSH", "User name of the administrator of the web interface": "Užívateľské meno správcu webového rozhrania", - "User name of the deprecated Flowise application login (only read by Flowise versions before 3.0.1)": "Užívateľské meno depreciovaného prihlasovacieho protokolu aplikácie Flowise (prečítané iba verziami Flowise pred 3.0.1)", + "User name of the deprecated Flowise application login (only read by Flowise versions before 3.0.1)": "Používateľské meno zastaraného prihlasovania aplikácie Flowise (používajú iba verzie pred 3.0.1)", "User of the AdGuard Home that receives the settings": "Užívateľ AdGuard Home, ktorý prijíma nastavenia", "User of the MariaDB server": "Používateľ servera MariaDB", "User of the PostgreSQL server": "Používateľ servera PostgreSQL", "User of the database server (mysql and pgsql only)": "Používateľ databázového servera (iba mysql a pgsql)", "User of the main AdGuard Home": "Užívateľ hlavného AdGuard Home", - "User-friendly WebUI for LLMs (Formerly Ollama WebUI)": "Užívateľsky prívetivé WebUI pre LLM (predovšetkým Ollama WebUI)", + "User-friendly WebUI for LLMs (Formerly Ollama WebUI)": "Používateľsky prívetivé webové rozhranie pre LLM (predtým Ollama WebUI)", "Username": "Používateľské meno", "Username (e.g. root@pam or user@pbs!token):": "Používateľské meno (napr. root@pam alebo user@pbs!token):", "Username / token": "Používateľské meno / token", @@ -7347,7 +7350,7 @@ "Utilities and Tools": "Nástroje", "Utilities installation completed": "Inštalácia nástrojov je dokončená", "Utilities installed by ProxMenux have been removed": "Pomôcky nainštalované používateľom ProxMenux boli odstránené", - "VA-API driver": "Vodič VA-API", + "VA-API driver": "Ovládač VA-API", "VA-API render device": "VA-API vykresľovacie zariadenie", "VA-API video acceleration": "VA-API video zrýchlenie", "VFIO device IDs removed from /etc/modprobe.d/vfio.conf": "ID VFIO zariadení boli odstránené z /etc/modprobe.d/vfio.conf", @@ -7359,7 +7362,7 @@ "VFIO orphans cleared but initramfs rebuild failed; check /var/log/proxmenux logs.": "Osirelé VFIO väzby boli vyčistené, ale znovuvytvorenie initramfs zlyhalo; skontrolujte záznamy v /var/log/proxmenux.", "VFS cache mode": "Režim vyrovnávacej pamäte VFS", "VLAN": "VLAN", - "VLC Media Player is a free and open source cross-platform multimedia player and framework that delivers dependable performance across multiple devices.": "VLC Médiá Hráč je voľný a otvorený zdroj multimediálneho prehrávača a rámca, ktorý poskytuje spoľahlivý výkon vo viacerých zariadeniach.", + "VLC Media Player is a free and open source cross-platform multimedia player and framework that delivers dependable performance across multiple devices.": "VLC Media Player je bezplatný multiplatformový multimediálny prehrávač a rámec s otvoreným zdrojovým kódom, ktorý poskytuje spoľahlivý výkon na rôznych zariadeniach.", "VM": "VM", "VM Conflict Policy": "Riešenie konfliktu VM", "VM ID": "ID VM", @@ -7393,16 +7396,16 @@ "VMID in use": "VMID sa už používa", "VMID must be a number.": "VMID musí byť číslo.", "VMID of the Borg server LXC on": "VMID Borg server LXC na", - "VMID of the Rclone OCI container": "VMID nádoby Rclone OCI", + "VMID of the Rclone OCI container": "VMID OCI kontajnera Rclone", "VMs to destroy:": "VM na zničenie:", "VMs, LXCs, network, /etc/pve, users, cron, packages, drivers, ProxMenux state, etc.": "VM, LXC, sieť, /etc/pve, používatelia, cron, balíky, ovládače, stav ProxMenux atď.", - "VS Code is an integrated development environment developed by Microsoft. This container runs the full desktop application, for a web native version see Code Server.": "VS Code je integrované vývojové prostredie vyvinuté spoločnosťou Microsoft. Tento kontajner spúšťa plnú desktopovú aplikáciu, pre web natívne verziu pozri Code Server.", - "VSCodium is a community-driven, freely-licensed binary distribution of Microsoft’s editor VS Code.": "VSCodium je komunita-riadený, voľne licencované binárne distribúcie Microsoft a editor VS Kód.", + "VS Code is an integrated development environment developed by Microsoft. This container runs the full desktop application, for a web native version see Code Server.": "VS Code je integrované vývojové prostredie od Microsoftu. Tento kontajner spúšťa úplnú desktopovú aplikáciu. Verziu určenú priamo pre web nájdete pod názvom Code Server.", + "VSCodium is a community-driven, freely-licensed binary distribution of Microsoft’s editor VS Code.": "VSCodium je komunitne spravovaná binárna distribúcia editora VS Code od Microsoftu so slobodnou licenciou.", "Valid backups for all VMs/CTs": "Platné zálohy všetkých VM/CT", "Validating Proxmox 9 repositories (checking 'proxmox-ve' candidate)...": "Overujem Proxmox 9 repozitáre (kontrolujem kandidáta 'proxmox-ve')...", "Validating credentials with server": "Overujem prihlasovacie údaje na serveri", "Validating disk safety...": "Overujem bezpečnosť disku...", - "Validation method: http (port 80 forwarded) or dns (DNS provider plugin)": "Validačná metóda: http (port 80 expedované) alebo dns (DNS modul poskytovateľa)", + "Validation method: http (port 80 forwarded) or dns (DNS provider plugin)": "Spôsob overenia: http (presmerovaný port 80) alebo dns (doplnok poskytovateľa DNS)", "Value for": "Hodnota pre", "Variable name": "Názov premennej", "Variables": "Premenné", @@ -7435,7 +7438,7 @@ "Version:": "Verzia:", "Version: Auto-negotiation (NFSv3/NFSv4)": "Verzia: automatické dohodnutie (NFSv3/NFSv4)", "Versions shown belong to maintained NVIDIA branches that list your GPU PCI ID and are new enough to build against the running kernel. DKMS compilation is the final validation. The recommended version keeps the current branch, or uses the NVIDIA Production Branch on a fresh install.": "Zobrazené verzie patria do udržiavaných pobočiek NVIDIA, ktoré uvádzajú vaše ID PCI GPU a sú dostatočne nové na to, aby sa dali postaviť proti bežiacemu jadru. Kompilácia DKMS je konečným overením. Odporúčaná verzia ponecháva aktuálnu vetvu alebo používa NVIDIA Production Branch pri novej inštalácii.", - "Video acceleration and object detection are independent choices; the installer does not write camera or detector YAML.": "Video zrýchlenie a detekcia objektu sú nezávislé voľby; inštalátor nepíše fotoaparát alebo detektor YAML.", + "Video acceleration and object detection are independent choices; the installer does not write camera or detector YAML.": "Zrýchlenie videa a detekcia objektov sú nezávislé voľby. Inštalačný sprievodca nezapisuje nastavenia kamier ani detektorov do YAML.", "Video transcoding acceleration": "Zrýchlenie priepustnosti videa", "Video transcoding profile not implemented:": "Profil transkódovania videa nie je implementovaný:", "View CIFS Mounts (pvesm + fstab)": "Zobraziť CIFS mounty (pvesm + fstab)", @@ -7474,14 +7477,14 @@ "Visit https://osx-proxmox.com for more information.": "Viac informácií nájdete na https://osx-proxmox.com.", "Visit the website to discover more scripts, stay updated with the latest updates, and support the project:": "Navštívte web, kde nájdete ďalšie skripty, novinky a môžete podporiť projekt:", "Visit the website to discover more scripts, stay updated with the latest updates, and support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE": "Navštívte web, kde nájdete ďalšie skripty, novinky a môžete podporiť projekt:\n\nhttps://community-scripts.github.io/ProxmoxVE", - "Vivaldi is a Norwegian freeware, cross-platform web browser with a built-in email client developed by Vivaldi Technologies.": "Vivaldi je nórsky freeware, cross-platform webový prehliadač s vstavaným e-mailovým klientom vyvinutým Vivaldi Technologies.", + "Vivaldi is a Norwegian freeware, cross-platform web browser with a built-in email client developed by Vivaldi Technologies.": "Vivaldi je bezplatný multiplatformový webový prehliadač so vstavaným e-mailovým klientom, ktorý vyvíja nórska spoločnosť Vivaldi Technologies.", "Volume configuration cancelled": "Nastavenie dátového úložiska bolo zrušené", - "Volume options are not yet supported": "Možnosti objemu zatiaľ nie sú podporované", + "Volume options are not yet supported": "Voľby dátových zväzkov zatiaľ nie sú podporované", "Volume size in GB": "Veľkosť dátového disku v GB", "Volumes attached": "Pripojené objemy", "Volumes prepared for the first start:": "Objemy pripravené na prvý začiatok:", - "Volumes shared between services are not yet supported": "Objemy zdieľané medzi službami zatiaľ nie sú podporované", - "Vscodium-web is a community-driven, freely-licensed binary distribution of the remote host web component of Microsoft's editor VS Code.": "Vscodium-web je komunitne riadený, voľne licencovaná binárna distribúcia vzdialenej webovej zložky Microsoft editor VS Code.", + "Volumes shared between services are not yet supported": "Dátové zväzky zdieľané medzi službami zatiaľ nie sú podporované", + "Vscodium-web is a community-driven, freely-licensed binary distribution of the remote host web component of Microsoft's editor VS Code.": "VSCodium Web je komunitne spravovaná binárna distribúcia vzdialenej webovej súčasti editora VS Code od Microsoftu so slobodnou licenciou.", "Vulnerability detection": "hľadania zraniteľností", "WARNING": "UPOZORNENIE", "WARNING — This backup contains paths that are risky to restore on a running system:": "UPOZORNENIE - táto záloha obsahuje cesty, ktoré je rizikové obnovovať na bežiacom systéme:", @@ -7506,7 +7509,7 @@ "WARNING: this will ERASE EVERYTHING on the disk.": "UPOZORNENIE: týmto sa VYMAŽE VŠETKO na disku.", "WEB UI to manage WireGuard VPN.": "WEB UI pre správu WireGuard VPN.", "WILL BE PERMANENTLY ERASED.": "BUDÚ NATRVALO VYMAZANÉ.", - "WPS Office is a lightweight, feature-rich comprehensive office suite with high compatibility. As a handy and professional office software, WPS Office allows you to edit files in Writer, Presentation, Spreadsheet, and PDF to improve your work efficiency.": "WPS Office je ľahký, vybavený komplexný kancelársky apartmán s vysokou kompatibilitou. Ako praktický a profesionálny kancelársky softvér vám WPS Office umožňuje upravovať súbory v programe Writer, Prezentácia, Spreadsheet, a PDF pre zlepšenie vašej pracovnej efektívnosti.", + "WPS Office is a lightweight, feature-rich comprehensive office suite with high compatibility. As a handy and professional office software, WPS Office allows you to edit files in Writer, Presentation, Spreadsheet, and PDF to improve your work efficiency.": "WPS Office je nenáročný kancelársky balík s množstvom funkcií a vysokou kompatibilitou. Umožňuje efektívne upravovať textové dokumenty, prezentácie, tabuľky a súbory PDF.", "Wait for each node to complete before starting next": "Pred začiatkom ďalšieho uzla počkajte, kým aktuálny skončí", "Waiting for Home Assistant OS...": "Čakám na Home Assistant OS...", "Waiting for the FUSE mount:": "Čakám na FUSE mount:", @@ -7526,7 +7529,7 @@ "Watchdog (on): restart the application when it crashes": "Watchdog (zapnutý): reštartovať aplikáciu, keď zlyhá", "Watchdog disabled.": "Watchdog vypnutý.", "Watchdog enabled: the application is restarted when it crashes.": "Watchdog zapnutý: aplikácia sa pri zlyhaní reštartuje.", - "WeKnora is an LLM-powered framework designed for deep document understanding and semantic retrieval, especially for handling complex, heterogeneous documents.": "WeKnora je LLM-poháňaný rámec určený pre hlboké pochopenie dokumentov a sémantické vyhľadávanie, najmä pre manipuláciu complex, heterogénne dokumenty.", + "WeKnora is an LLM-powered framework designed for deep document understanding and semantic retrieval, especially for handling complex, heterogeneous documents.": "WeKnora je rámec využívajúci veľké jazykové modely (LLM) na dôkladné porozumenie dokumentom a sémantické vyhľadávanie, najmä v zložitých dokumentoch rôzneho typu.", "Web UI": "Web UI", "Web UI 1": "Web UI 1", "Web UI 2": "Web UI 2", @@ -7536,14 +7539,14 @@ "Web address of the AdGuard Home that receives the settings (e.g. http://192.168.1.3)": "Webová adresa AdGuard Home, ktorá prijíma nastavenia (napr. http://192.168.1.3)", "Web address of the main AdGuard Home, whose settings are copied (e.g. http://192.168.1.2)": "Webová adresa hlavného AdGuard Home, ktorého nastavenia sú kopírované (napr. http://192.168.1.2)", "Web interface to manage devices running Tasmota firmware.": "Webové rozhranie pre správu zariadení bežiacich na Tasmota firmware.", - "WebCord can be summarized as a pack of security and privacy hardenings, Discord features reimplementations, Electron / Chromium / Discord bugs workarounds, stylesheets, internal pages and wrapped https://discord.com page, designed to conform with ToS as much as it is possible (or hide the changes that might violate it from Discord's eyes).": "WebCord možno zhrnúť ako balík bezpečnostných a súkromia kalenie, Discord funkcie reimplementácie, Electron / Chromium / Discord chyby práce, štýlové listy, vnútorné stránky a zabalené https://discord.com stránky, navrhnutý tak, aby zodpovedali ToS tak, ako je to možné (alebo skryť zmeny, ktoré by mohli porušovať z očí Discord).", - "Webgrabplus is a multi-site incremental xmltv epg grabber. It collects tv-program guide data from selected tvguide sites for your favourite channels.": "Webgrabplus je multi-site prírastkové xmltv epg grabber. Zhromažďuje tv-program sprievodcu dátami z vybraných tvguide stránok pre vaše obľúbené kanály.", + "WebCord can be summarized as a pack of security and privacy hardenings, Discord features reimplementations, Electron / Chromium / Discord bugs workarounds, stylesheets, internal pages and wrapped https://discord.com page, designed to conform with ToS as much as it is possible (or hide the changes that might violate it from Discord's eyes).": "WebCord je klient obklopujúci stránku https://discord.com, ktorý zlepšuje bezpečnosť a súkromie, upravuje funkcie Discordu, obchádza chyby Electronu, Chromia a Discordu a pridáva štýly aj interné stránky. Snaží sa čo najviac dodržiavať podmienky používania alebo skryť pred Discordom úpravy, ktoré by ich mohli porušovať.", + "Webgrabplus is a multi-site incremental xmltv epg grabber. It collects tv-program guide data from selected tvguide sites for your favourite channels.": "WebGrab+Plus priebežne získava elektronický programový sprievodca (EPG) vo formáte XMLTV z viacerých webov. Zhromažďuje televízny program vybraných staníc z podporovaných stránok.", "Webservers & Proxies": "Webové servery a proxy", "Website": "Webová stránka", - "Webstation is a web native emulation focused LXQt desktop based on Ubuntu.": "Webstation je webová natívne emulácia zameraná LXQt plocha na základe Ubuntu.", - "Webtop - Alpine, Ubuntu, Fedora, and Arch based containers containing full desktop environments in officially supported flavors accessible via any modern web browser.": "Webtop - Alpine, Ubuntu, Fedora, a Arch založené kontajnery obsahujúce plné pracovné prostredie v oficiálne podporovaných príchutiach prístupných cez akýkoľvek moderný webový prehliadač.", + "Webstation is a web native emulation focused LXQt desktop based on Ubuntu.": "Webstation poskytuje pracovnú plochu LXQt založenú na Ubuntu, prístupnú cez web a zameranú na emuláciu.", + "Webtop - Alpine, Ubuntu, Fedora, and Arch based containers containing full desktop environments in officially supported flavors accessible via any modern web browser.": "Webtop poskytuje kontajnery založené na Alpine, Ubuntu, Fedore a Archu s úplnými pracovnými prostrediami v oficiálne podporovaných variantoch, prístupnými cez moderný webový prehliadač.", "Weekly, Sunday 02:00": "Týždenne, nedeľa 02:00", - "Weixin (WeChat) is an instant messaging, social media, and mobile payment app developed by Tencent.": "Weixin (WeChat) je okamžité správy, sociálne médiá, a mobilné platobné aplikácie vyvinuté Tencent.", + "Weixin (WeChat) is an instant messaging, social media, and mobile payment app developed by Tencent.": "Weixin (WeChat) je aplikácia na okamžité správy, sociálne siete a mobilné platby vyvíjaná spoločnosťou Tencent.", "What cannot be translated:": "Čo nemožno previesť na nastavenia LXC:", "What do you want to change?": "Čo chcete zmeniť?", "What do you want to do?": "Čo chcete urobiť?", @@ -7572,26 +7575,26 @@ "Will be configured now": "Nastaví sa teraz", "Windows Installation Options": "Možnosti inštalácie Windowsu", "Windows path:": "Cesta pre Windows:", - "WineGUI is a user-interface friendly Wine manager that provides a graphical frontend for creating and managing Wine bottles.": "WineGUI je užívateľsky ústretový Wine manager, ktorý poskytuje grafický frontend pre vytváranie a správu fliaš vína.", + "WineGUI is a user-interface friendly Wine manager that provides a graphical frontend for creating and managing Wine bottles.": "WineGUI je používateľsky prívetivý správca Wine s grafickým rozhraním na vytváranie a správu oddelených prostredí Wine (bottles).", "Wipe all — erase partitions + metadata": "Vymazať všetko - odstrániť oddiely + metadáta", "Wipe all — remove partitions + metadata": "Vymazať všetko - odstrániť oddiely + metadáta", "Wipe old signatures and partition table (DESTRUCTIVE):": "Vymazať staré podpisy a tabuľku oddielov (DEŠTRUKTÍVNE):", "Wiping existing partition table...": "Vymazávam existujúcu tabuľku oddielov...", "Wiping partitions and metadata...": "Vymazávam oddiely a metadáta...", "WireGuard Easy web interface": "Webové rozhranie WireGuard Easy", - "WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances. Initially released for the Linux kernel, it is now cross-platform (Windows, macOS, BSD, iOS, Android) and widely deployable. It is currently under heavy development, but already it might be regarded as the most secure, easiest to use, and simplest VPN solution in the industry.": "WireGuard® je extrémne jednoduchá, ale rýchla a moderná VPN, ktorá využíva najmodernejšiu kryptografiu. Jeho cieľom je byť rýchlejší, jednoduchší, štíhlejší a užitočnejší ako IPsec, pričom sa vyhýba masívnej bolesti hlavy. Má v úmysle byť výrazne výkonnejší ako OpenVPN. WireGuard je navrhnutý ako všeobecný účel VPN pre prevádzku na vstavaných rozhraniach a super počítačoch podobne, vhodný pre mnoho rôznych okolností. Spočiatku sa vydáva pre jadro Linuxu, je teraz cross-platform (Windows, macOS, BSD, iOS, Android) a široko rozmiestnený. To je v súčasnosti v ťažkom vývoji, ale už to môže byť považované za najbezpečnejšie, najjednoduchšie použitie, a najjednoduchšie riešenie VPN v priemysle.", + "WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances. Initially released for the Linux kernel, it is now cross-platform (Windows, macOS, BSD, iOS, Android) and widely deployable. It is currently under heavy development, but already it might be regarded as the most secure, easiest to use, and simplest VPN solution in the industry.": "WireGuard® je jednoduchá, rýchla a moderná VPN využívajúca súčasnú kryptografiu. Je navrhnutá ako jednoduchšia a menej náročná alternatíva k IPsec s cieľom prekonať výkon OpenVPN. Hodí sa pre vstavané zariadenia aj superpočítače. Pôvodne vznikla pre jadro Linuxu, dnes podporuje aj Windows, macOS, BSD, iOS a Android. Napriek pokračujúcemu vývoju je považovaná za bezpečné a ľahko použiteľné riešenie VPN.", "Wired NICs in backup missing on target:": "Káblové sieťové karty zo zálohy chýbajú na cieli:", - "Wireshark is the world’s foremost and widely-used network protocol analyzer. It lets you see what’s happening on your network at a microscopic level and is the de facto (and often de jure) standard across many commercial and non-profit enterprises, government agencies, and educational institutions. Wireshark development thrives thanks to the volunteer contributions of networking experts around the globe and is the continuation of a project started by Gerald Combs in 1998.": "Wireshark je prvoradý a široko používaný sieťový protokol analyzátor. Umožňuje vidieť, čo sa deje na vašej sieti na mikroskopickej úrovni a je de facto (a často de jure) štandard v mnohých komerčných a neziskových podnikoch, vládnych agentúr a vzdelávacích inštitúcií. Rozvoj Wireshark sa darí vďaka dobrovoľným príspevkom odborníkov na vytváranie sietí po celom svete a je pokračovaním projektu, ktorý začal Gerald Combs v roku 1998.", + "Wireshark is the world’s foremost and widely-used network protocol analyzer. It lets you see what’s happening on your network at a microscopic level and is the de facto (and often de jure) standard across many commercial and non-profit enterprises, government agencies, and educational institutions. Wireshark development thrives thanks to the volunteer contributions of networking experts around the globe and is the continuation of a project started by Gerald Combs in 1998.": "Wireshark je široko používaný analyzátor sieťových protokolov. Umožňuje podrobne sledovať prevádzku v sieti a je štandardným nástrojom vo firmách, neziskových organizáciách, úradoch aj školách. Vďaka dobrovoľným príspevkom sieťových odborníkov z celého sveta pokračuje v projekte, ktorý Gerald Combs začal v roku 1998.", "With another storage they stop running.": "S iným úložiskom prestanú fungovať.", "With another storage this job no longer starts them. Another job that writes to the original storage still does.": "S iným úložiskom ich už táto úloha nespúšťa. Iná úloha, ktorá zapisuje do pôvodného úložiska, ich spúšťa naďalej.", - "With dns validation, the DNSPLUGIN variable names the provider plugin. The advanced installation asks for it; otherwise add the line lxc.environment.runtime: DNSPLUGIN= to /etc/pve/lxc/.conf with the container stopped.": "Pri validácii dns, DNSPLUGIN premenná názvy poskytovateľa plugin. Pokročilá inštalácia si to vyžaduje; inak pridajte riadok lxc.environment.runtime: DNSPLUGIN= k /etc/pve/lxc/.conf s kontajnerom zastavil.", - "With dns validation, write the provider credentials in /config/dns-conf/.ini inside the container and restart it.": "S validáciou dns, napíšte poskytovateľa credentials v /config/dns-conf/.ini vnútri kontajnera a reštartujte ho.", - "With http validation, port 80 of the router must be forwarded to port 80 of this container.": "Pri validácii http sa port 80 routeru musí zaslať do prístavu 80 tohto kontajnera.", + "With dns validation, the DNSPLUGIN variable names the provider plugin. The advanced installation asks for it; otherwise add the line lxc.environment.runtime: DNSPLUGIN= to /etc/pve/lxc/.conf with the container stopped.": "Pri overení dns určuje premenná DNSPLUGIN doplnok poskytovateľa. Pokročilá inštalácia sa naň opýta. Inak pri zastavenom kontajneri pridajte do /etc/pve/lxc/.conf riadok lxc.environment.runtime: DNSPLUGIN=.", + "With dns validation, write the provider credentials in /config/dns-conf/.ini inside the container and restart it.": "Pri overení dns zapíšte prihlasovacie údaje poskytovateľa do /config/dns-conf/.ini v kontajneri a kontajner reštartujte.", + "With http validation, port 80 of the router must be forwarded to port 80 of this container.": "Pri overení http musí byť port 80 smerovača presmerovaný na port 80 tohto kontajnera.", "With warnings": "S upozorneniami", "Without Function Level Reset (FLR), passthrough is not considered reliable": "Bez Function Level Reset (FLR) sa passthrough nepovažuje za spoľahlivý", "Without a usable reset path, passthrough reliability is poor and VM": "Bez použiteľnej reset cesty je spoľahlivosť passthrough nízka a VM", "Wizard cancelled": "Sprievodca zrušený", - "Wolfenstein: Blade of Agony is a story-driven WWII shooter inspired by Wolfenstein and Doom.": "Wolfenstein: Blade of Agony je príbeh-riadený WWII strelec inšpirovaný Wolfenstein a Doom.", + "Wolfenstein: Blade of Agony is a story-driven WWII shooter inspired by Wolfenstein and Doom.": "Wolfenstein: Blade of Agony je príbehová strieľačka z druhej svetovej vojny inšpirovaná hrami Wolfenstein a Doom.", "Workflow automation tool": "Nástroj na automatizáciu pracovného toku", "Working directory:": "Pracovný priečinok:", "Works with LVM, ZFS, and BTRFS storage types": "Funguje s typmi úložísk LVM, ZFS a BTRFS", @@ -7607,7 +7610,7 @@ "Wrong inherited registry lock": "Nesprávny zdedený register", "Wrong passphrase": "Nesprávna fráza", "Wrong size in": "Nesprávna veľkosť", - "Xbackbone is a simple, self-hosted, lightweight PHP file manager that support the instant sharing tool ShareX and *NIX systems. It supports uploading and displaying images, GIF, video, code, formatted text, and file downloading and uploading. Also have a web UI with multi user management, past uploads history and search support.": "Xbackbone je jednoduchý, self-hosted, ľahký PHP správca súborov, ktoré podporujú nástroj okamžité zdieľanie ShareX a *NIX systémy. Podporuje nahrávanie a zobrazovanie obrázkov, GIF, video, kód, formátovaný text a sťahovanie a nahrávanie súborov. Tiež majú webové UI s multi užívateľským manažmentom, minulé nahráva históriu a podporu vyhľadávania.", + "Xbackbone is a simple, self-hosted, lightweight PHP file manager that support the instant sharing tool ShareX and *NIX systems. It supports uploading and displaying images, GIF, video, code, formatted text, and file downloading and uploading. Also have a web UI with multi user management, past uploads history and search support.": "XBackBone je jednoduchý a nenáročný správca súborov v PHP na vlastnom serveri s podporou ShareX a systémov *NIX. Podporuje nahrávanie, zobrazovanie a sťahovanie obrázkov, GIF, videa, kódu a formátovaného textu. Webové rozhranie ponúka správu viacerých používateľov, históriu nahratých súborov a vyhľadávanie.", "Yaak is a desktop API client for organizing and executing REST, GraphQL, and gRPC requests. It's built using Tauri, Rust, and ReactJS.": "Yaak je desktopový API klient pre organizovanie a vykonávanie REST, GraphQL, a gRPC požiadavky. Je postavený pomocou Tauri, Rust a ReactJS.", "Yes": "Áno", "Yes, upload": "Áno, nahrať", @@ -7639,9 +7642,9 @@ "You should now be able to access the Proxmox web interface.": "Teraz by ste mali mať prístup k webovému rozhraniu Proxmoxu.", "You will need a Tailscale auth key from: https://login.tailscale.com/admin/settings/keys": "Budete potrebovať Tailscale auth key z: https://login.tailscale.com/admin/settings/keys", "Your Coral USB device and its runtime (libedgetpu1) will NOT be affected.": "Vaše zariadenie Coral USB a jeho runtime (libedgetpu1) NEBUDÚ ovplyvnené.", - "Your machine learning Env work with Jupyter Lab": "Vaše strojové učenie Env práce s Jupyter Lab", + "Your machine learning Env work with Jupyter Lab": "Prostredie strojového učenia s JupyterLab", "Your next YouTube media manager": "Váš ďalší manažér médií YouTube", - "Your_spotify is a self-hosted application that tracks what you listen and offers you a dashboard to explore statistics about it! It's composed of a web server which polls the Spotify API every now and then and a web application on which you can explore your statistics.": "Your_spotify je self-hosted aplikácie, ktorá sleduje to, čo počúvate a ponúka palubnú dosku preskúmať štatistiky o tom! Skladá sa z webového servera, ktorý skúma API Spotify sem tam a tam a webovej aplikácie, na ktorej môžete preskúmať svoje štatistiky.", + "Your_spotify is a self-hosted application that tracks what you listen and offers you a dashboard to explore statistics about it! It's composed of a web server which polls the Spotify API every now and then and a web application on which you can explore your statistics.": "Your_spotify je aplikácia na vlastnom serveri, ktorá sleduje počúvanie hudby a zobrazuje štatistiky. Tvorí ju server pravidelne zisťujúci údaje cez Spotify API a webové rozhranie na ich prezeranie.", "ZFS ARC config removed (kernel defaults will apply on reboot)": "Nastavenie ZFS ARC bolo odstránené (predvolené hodnoty kernelu sa použijú po reštarte)", "ZFS ARC maximum configured:": "Maximálna veľkosť ZFS ARC je nastavená na:", "ZFS ARC optimization completed": "Optimalizácia ZFS ARC je dokončená", @@ -7685,13 +7688,13 @@ "Zero all data — partition table preserved, data wiped": "Vynulovať všetky dáta - tabuľka oddielov zostane, dáta sa vymažú", "Zero all data — partition table preserved": "Vynulovať všetky dáta - tabuľka oddielov zostane", "Zeroing partition": "Nulujem oddiel", - "Znc is an IRC network bouncer or BNC. It can detach the client from the actual IRC server, and also from selected channels. Multiple clients from different locations can connect to a single ZNC account simultaneously and therefore appear under the same nickname on IRC.": "Znc je vyhadzovač siete IRC alebo BNC. Môže odpojiť klienta od aktuálneho IRC servera, ako aj od vybraných kanálov. Viacerí klienti z rôznych miest sa môžu pripojiť na jeden ZNC účet súčasne, a preto sa objaví pod rovnakou prezývkou na IRC.", + "Znc is an IRC network bouncer or BNC. It can detach the client from the actual IRC server, and also from selected channels. Multiple clients from different locations can connect to a single ZNC account simultaneously and therefore appear under the same nickname on IRC.": "ZNC je sprostredkujúci server pre IRC (bouncer, BNC). Udržiava spojenie so serverom a kanálmi nezávisle od klienta. Viacerí klienti z rôznych miest sa môžu súčasne pripojiť k jednému účtu ZNC a vystupovať na IRC pod rovnakou prezývkou.", "Zotero is a free, easy-to-use tool to help you collect, organize, annotate, cite, and share research.": "Zotero je bezplatný, ľahko použiteľný nástroj, ktorý vám pomôže zhromažďovať, organizovať, anotovať, citovať a zdieľať výskum.", "a device it asks for cannot be translated:": "požadované zariadenie nemožno pripojiť do LXC:", "a different host monitor include already exists:": "už existuje iný include hostiteľského monitora:", "a value is required": "hodnota je povinná", "aMule WebUI (password only, no username)": "aMule WebUI (iba heslo, žiadne užívateľské meno)", - "aMule is a multi-platform client for the ED2K file sharing network and based on the windows client eMule. aMule started in August 2003, as a fork of xMule, which is a fork of lMule.": "aMule je multiplatformový klient pre sieť zdieľania súborov ED2K a založený na eMule klienta okien. aMule začal v auguste 2003, ako fork xMule, čo je fork lMule.", + "aMule is a multi-platform client for the ED2K file sharing network and based on the windows client eMule. aMule started in August 2003, as a fork of xMule, which is a fork of lMule.": "aMule je multiplatformový klient siete ED2K na zdieľanie súborov, založený na klientovi eMule pre Windows. Vznikol v auguste 2003 ako odnož xMule, ktorý vychádzal z lMule.", "active VF(s)": "aktívne VF", "active VFs": "aktívnymi VF", "active Virtual Functions. Changing its driver binding would destroy every VF.": "aktívnymi Virtual Functions. Zmena naviazania ovládača by zrušila každú VF.", @@ -7724,7 +7727,7 @@ "apt-get exited": "apt-get skončil", "apt-get update returned warnings. Continuing anyway; check": "apt-get update vrátil varovania. Pokračujem ďalej; skontrolujte", "as": "ako", - "assembling": "montáž", + "assembling": "zostavovanie", "automatically. Install it manually inside the container.": "automaticky. Nainštalujte ho ručne vo vnútri kontajnera.", "automatically. Reboot LXC to fully release.": "automaticky. Pre úplné uvoľnenie reštartujte LXC.", "available": "K dispozícii", @@ -7736,7 +7739,7 @@ "blocking issue(s).": "blokujúcich problémov.", "btrfs — Proxmox dir storage (snapshots, compression)": "btrfs — Proxmox dir úložisko (snapshoty, kompresia)", "btrfs — snapshots and compression": "btrfs - snapshoty a kompresia", - "budge is an open source 'budgeting with envelopes' personal finance app.": "budge je otvorený zdroj \"budgeting s obálkami\" aplikácie osobného financovania.", + "budge is an open source 'budgeting with envelopes' personal finance app.": "budge je aplikácia na správu osobných financií s otvoreným zdrojovým kódom, ktorá používa obálkovú metódu rozpočtu.", "builds against kernel": "stavia proti jadru", "but it does not match the one used to create the backup. Replace it with the correct keyfile from the source host and retry.": "ale nezhoduje sa s tým, ktorý bol použitý pri vytvorení zálohy. Nahraďte ho správnym keyfile zo zdrojového hosta a skúste to znova.", "bytes": "bajtov", @@ -7754,7 +7757,7 @@ "custom": "vlastné", "custom dependency commands are not yet supported": "Vlastné príkazy závislosti ešte nie sú podporované", "custom path(s) saved.": "uložených vlastných ciest.", - "darktable is an open source photography workflow application and raw developer. A virtual lighttable and darkroom for photographers. It manages your digital negatives in a database, lets you view them through a zoomable lighttable and enables you to develop raw images and enhance them.": "darktable je aplikácia fotografií s otvoreným zdrojom a surový vývojár. Virtuálna svetelná a tmavá miestnosť pre fotografov. Spravuje vaše digitálne negatíva v databáze, umožňuje vám ich prezerať pomocou zoomovateľného svetelného zariadenia a umožňuje vám vyvinúť surové obrázky a zlepšiť ich.", + "darktable is an open source photography workflow application and raw developer. A virtual lighttable and darkroom for photographers. It manages your digital negatives in a database, lets you view them through a zoomable lighttable and enables you to develop raw images and enhance them.": "darktable je fotografická aplikácia s otvoreným zdrojovým kódom na správu pracovného postupu a spracovanie RAW. Slúži ako virtuálny prehliadací stôl a tmavá komora: digitálne negatívy spravuje v databáze, umožňuje ich prehliadať s priblížením, vyvolávať a upravovať.", "ddclient starts with the example configuration and updates nothing yet. Write your provider, login and domains in /config/ddclient.conf inside the container, then restart it.": "ddclient začína s konfiguráciou príkladu a ešte nič neaktualizuje. Napíšte svojho poskytovateľa, prihlásenie a domény do /config/ddclient.conf vnútri kontajnera a potom ho reštartujte.", "default": "predvolené", "delete the credentials file (if any)": "vymazať súbor s prihlasovacími údajmi (ak existuje)", @@ -7764,7 +7767,7 @@ "devices": "zariadenia", "devices (dynamic runtime)": "zariadenia (dynamický čas chodu)", "did not become ready. Skipping.": "nebol pripravený. Preskakujem.", - "digiKam: Professional Photo Management with the Power of Open Source": "digiKam: Profesionálny Photo Management s výkonom otvoreného zdroja", + "digiKam: Professional Photo Management with the Power of Open Source": "digiKam: profesionálna správa fotografií so silou otvoreného zdrojového kódu", "disabled": "zakázané", "disk(s) added to CT": "diskov pridaných do CT", "disk(s) added to VM": "diskov pridaných do VM", @@ -7776,7 +7779,7 @@ "docker run command of the application": "príkaz docker run aplikácie", "does not exist on this host. Path not added.": "na tomto hostovi neexistuje. Cesta nebola pridaná.", "does not exist. Exiting.": "neexistuje. Končím.", - "doplarr_rs starts from the example configuration and connects to nothing. Write the token of your Discord bot in discord_token in /config/config.toml inside the container.": "doplarr_rs začína od konfigurácie príkladu a spája sa s ničím. Napíšte token svojho Discord bot v discord token v /config/config.toml vnútri kontajnera.", + "doplarr_rs starts from the example configuration and connects to nothing. Write the token of your Discord bot in discord_token in /config/config.toml inside the container.": "doplarr_rs začína s ukážkovým nastavením a k ničomu sa nepripája. Token robota Discord zapíšte do discord_token v /config/config.toml v kontajneri.", "downloaded Compose file": "stiahnutého súboru Compose", "dpkg still reports unfinished package work; review": "dpkg stále hlási nedokončenú prácu s balíkom;recenzia", "driver components": "komponenty vodiča", @@ -7829,8 +7832,8 @@ "has a different MAC than the backup — update any DHCP static reservation": "má inú MAC než v zálohe - aktualizujte prípadnú statickú rezerváciu v DHCP", "has a new MAC": "má novú MAC", "has only": "má iba", - "health and persistence profile not yet defined": "profil zdravia a pretrvávania, zatiaľ nedefinovaný", - "hiSHtory is a better shell history. It stores your shell history in context (what directory you ran the command in, whether it succeeded or failed, how long it took, etc). This is all stored locally and end-to-end encrypted for syncing to to all your other computers.": "HISHtory je lepšia história mušlí. Ukladá históriu vášho shellu v kontexte (v akom adresári ste spustili príkaz, či sa to podarilo alebo zlyhalo, ako dlho to trvalo, atď). Toto všetko je uložené lokálne a end-to-end zašifrované pre synchronizáciu so všetkými ostatnými počítačmi.", + "health and persistence profile not yet defined": "profil kontroly stavu a trvalého úložiska zatiaľ nie je definovaný", + "hiSHtory is a better shell history. It stores your shell history in context (what directory you ran the command in, whether it succeeded or failed, how long it took, etc). This is all stored locally and end-to-end encrypted for syncing to to all your other computers.": "hiSHtory uchováva históriu príkazov shellu spolu s kontextom: adresárom, výsledkom aj trvaním príkazu. Údaje zostávajú uložené lokálne a pri synchronizácii medzi počítačmi sú chránené koncovým šifrovaním.", "host directory": "adresár hostiteľa", "host fstab only (not registered as Proxmox storage)": "iba host fstab (nie je zaregistrované ako úložisko Proxmoxu)", "hostpci entries for all IOMMU group devices": "hostpci položky pre všetky zariadenia IOMMU skupiny", @@ -7949,7 +7952,7 @@ "manually inside the container.": "ručne vo vnútri kontajnera.", "maximum performance": "maximálny výkon", "may be closed — trying discovery anyway...": "môže byť zatvorený - aj tak skúšam vyhľadanie...", - "melonDS aims at providing fast and accurate Nintendo DS emulation.": "Cieľom melonDS je poskytovať rýchle a accurate Nintendo DS emulácia.", + "melonDS aims at providing fast and accurate Nintendo DS emulation.": "melonDS sa zameriava na rýchlu a presnú emuláciu Nintendo DS.", "members:": "členovia:", "minimum": "minimum", "missing": "chýba", @@ -7961,12 +7964,12 @@ "mstream is a personal music streaming server. You can use mStream to stream your music from your home computer to any device, anywhere. There are mobile apps available for both Android and iPhone.": "mstream je osobný hudobný streamovací server. Môžete použiť mStream na streamovanie hudby z vášho domáceho počítača do akéhokoľvek zariadenia, kdekoľvek. K dispozícii sú mobilné aplikácie pre Android aj iPhone.", "must contain a valid numeric UID for the GPU": "musí obsahovať platné číselné UID pre GPU", "needed": "potrebné", - "needs a privileged LXC": "potrebuje privilegovanú LXC", + "needs a privileged LXC": "vyžaduje privilegovaný kontajner LXC", "needs a relaxed AppArmor or seccomp profile": "potrebuje uvoľnený AppArmor alebo seccomp profil", - "needs stack review": "Potreby stack review", + "needs stack review": "vyžaduje kontrolu zostavy", "needs these external services, already running:": "vyžaduje tieto už spustené externé služby:", "never": "nikdy", - "next free": "next free", + "next free": "ďalšie voľné", "next free block": "ďalší voľný blok", "nftables not available - using iptables ban action": "nftables nie je dostupné - používam zákaz cez iptables", "no": "nie", @@ -8025,11 +8028,11 @@ "pigz installed successfully": "pigz bol úspešne nainštalovaný", "pigz removed": "pigz bol odstránený", "pigz wrapper script created": "Wrapper skript pre pigz bol vytvorený", - "playit.gg has to claim this agent before it forwards anything. The agent prints a one-time claim link on the container console and keeps it there until the link is opened.": "playit.gg musí žiadať tohto agenta pred tým, než niečo postúpi. Agent vytlačí jednorázový reklamačný odkaz na konzolu kontajnera a nechá ho tam až do otvorenia odkazu.", + "playit.gg has to claim this agent before it forwards anything. The agent prints a one-time claim link on the container console and keeps it there until the link is opened.": "Pred presmerovaním prevádzky treba agenta priradiť k účtu playit.gg. Agent vypíše na konzole kontajnera jednorazový odkaz na priradenie a ponechá ho tam, kým ho neotvoríte.", "port": "port", "portmapper/rpcbind has been disabled": "portmapper/rpcbind bol zakázaný", - "private network assigned automatically": "privátne siete pridelené automaticky", - "privileged LXC": "Privilegované LXC", + "private network assigned automatically": "súkromná sieť pridelená automaticky", + "privileged LXC": "privilegovaný kontajner LXC", "proxmox-backup-client reported:": "proxmox-backup-client oznámil:", "proxmox-boot-tool refreshed": "proxmox-boot-tool bol obnovený", "pve-enterprise.list update skipped (no change)": "Aktualizácia pve-enterprise.list preskočená (bez zmeny)", @@ -8042,14 +8045,14 @@ "pvesm not found.": "pvesm sa nenašiel.", "pvesm path failed, trying manual detection...": "pvesm path zlyhal, skúšam ručné zistenie...", "pvesm status failed": "pvesm status zlyhal", - "pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad je Free and Open Source manažér download napísaný v Pythone a navrhnutý tak, aby bol veľmi ľahký, ľahko rozšíriteľný a plne zvládnuteľný cez web.", + "pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad je bezplatný správca sťahovania s otvoreným zdrojovým kódom napísaný v Pythone. Je nenáročný, ľahko rozšíriteľný a plne ovládateľný cez web.", "pyLoad web interface": "webové rozhranie pyLoad", "qBittorrent WebUI password (user: admin)": "qBittorrent WebUI heslo (používateľ: admin)", "qBittorrent already has a configuration; it is not overwritten": "qBittorrent už má konfiguráciu; nie je prepísaná", "qBittorrent configured": "Konfigurovaný qBittorrent", "qBittorrent did not apply the category:": "qBittorrent neuplatnila kategóriu:", "qBittorrent did not apply the download paths": "qBittorrent nepoužila cesty na stiahnutie", - "qBittorrent requires a non-empty password": "qBittorrent requires a non-prázdne heslo", + "qBittorrent requires a non-empty password": "qBittorrent vyžaduje neprázdne heslo", "qBittorrent: authenticated access to the preferences could not be verified": "qBittorrent: nemohol sa overiť overený prístup k preferenciám", "qBittorrent: invalid login response or missing session cookie": "qBittorrent: neplatná odpoveď prihlásenia alebo chýbajúci súbor cookie sedenia", "raw USB disk — no filesystem (will be FORMATTED)": "surový USB disk - bez súborového systému (bude NAformátovaný)", @@ -8059,7 +8062,7 @@ "recognition": "rozpoznávanie", "recognition only": "iba rozpoznávanie", "recommended": "odporúčané", - "recovering": "zotavenie", + "recovering": "obnova", "remapped users": "remapovanými používateľmi", "remove the (now-empty) directory if possible": "odstrániť teraz prázdny priečinok, ak to bude možné", "removed from Proxmox": "odstránené z Proxmoxu", @@ -8076,7 +8079,7 @@ "rpcbind could not be disabled completely": "rpcbind nebolo možné úplne vypnúť", "rpcbind service and socket have been disabled and stopped": "Služba rpcbind a socket boli deaktivované a zastavené", "rpcbind units were not found; no changes were made": "jednotky rpcbind neboli nájdené;neboli vykonané žiadne zmeny", - "rsnapshot starts with the default configuration, which backs up /data into /.snapshots. Edit /config/rsnapshot.conf inside the container to set your own backup points, snapshot root and retention intervals.": "rsnapshot začína štandardnou konfiguráciou, ktorá zálohuje / dáta do /.snapshots. Upraviť /config/rsnapshot.conf vnútri kontajnera nastaviť svoje vlastné záložné body, snímky koreňov a intervaly uchovávania.", + "rsnapshot starts with the default configuration, which backs up /data into /.snapshots. Edit /config/rsnapshot.conf inside the container to set your own backup points, snapshot root and retention intervals.": "rsnapshot začína s predvoleným nastavením, ktoré zálohuje /data do /.snapshots. V kontajneri upravte /config/rsnapshot.conf a nastavte vlastné zdroje záloh, koreňový adresár snímok a intervaly uchovávania.", "running": "beží", "runs in": "beží v", "safe paths now (configs, packages, /etc, /root, ...)": "bezpečné cesty teraz (nastavenia, balíky, /etc, /root, ...)", @@ -8119,14 +8122,14 @@ "suggested:": "navrhované:", "switch_gpu_mode.sh was not found.": "switch_gpu_mode.sh sa nenašiel.", "sysfs ROM dump failed — trying ACPI VFCT table...": "sysfs ROM dump zlyhal - skúšam ACPI VFCT tabuľku...", - "syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "syslog-ng umožňuje flexibilne zbierať, spracovať, klasifikovať, prepísať a korelovať protokoly z celej vašej infraštruktúry a ukladať alebo smerovať ich do log analytické nástroje.", + "syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "syslog-ng umožňuje flexibilne zbierať, spracúvať, triediť, upravovať a prepájať záznamy z celej infraštruktúry a ukladať ich alebo odosielať do nástrojov na analýzu denníkov.", "systemctl restart networking failed:": "systemctl restart networking zlyhal:", "systemd OnCalendar expression": "systemd OnCalendar výraz", "the API key was not generated on the first start": "kľúč API nebol vytvorený pri prvom štarte", "the NVIDIA hook of the container is not available:": "spúšťací skript NVIDIA pre tento kontajner nie je dostupný:", "the bridge does not exist on this host:": "most na tomto hostiteľovi neexistuje:", "the bridge exists on this host with another network:": "most existuje na tomto hostiteľovi s inou sieťou:", - "the container has its own address, so the port Docker published on the host is not needed": "kontajner má svoju vlastnú adresu, takže port Docker zverejnené na hostiteľa nie je potrebné", + "the container has its own address, so the port Docker published on the host is not needed": "kontajner má vlastnú adresu, takže port zverejnený Dockerom na hostiteľovi nie je potrebný", "the containers do not agree on the private network of": "kontajnery sa nezhodujú na súkromnej sieti", "the device does not exist on this host:": "zariadenie na tomto hostiteľovi neexistuje:", "the host directory does not exist:": "hostiteľský adresár neexistuje:", @@ -8155,9 +8158,9 @@ "unknown include:": "neznámy include:", "unknown mount hook:": "neznámy hook pripojenia:", "unprivileged LXC": "neprivilegovaný LXC", - "unsupported credential generator": "nepodporovaný credential generátor", - "unsupported dependency condition": "stav bez podpory závislosti", - "unsupported external credential or boolean": "nepodporované externé credential alebo boolean", + "unsupported credential generator": "nepodporovaný generátor prihlasovacích údajov", + "unsupported dependency condition": "nepodporovaná podmienka závislosti", + "unsupported external credential or boolean": "nepodporovaný externý prihlasovací údaj alebo logická hodnota", "unsupported variable": "nepodporovaná premenná", "updating": "aktualizácia", "updating NVIDIA userspace libs": "aktualizujem používateľské NVIDIA knižnice", @@ -8206,7 +8209,7 @@ "{count} saved accounts.": "{count} uložené účty.", "{path} already exists and belongs to another user of the host, so {app} cannot write to it.\n\nProxMenux can add access rules (ACL) to this directory, to everything inside it and to what is created there later, for the users and groups {app} runs as on the host (IDs {ids}). The owner of the files does not change and whatever reads or writes them today keeps doing so; the owner of the directory also gets access to the files {app} creates.\n\nGrant {app} access to this directory?": "{path} už existuje a patrí inému používateľovi hostiteľa, takže {app} naň nemôže zapisovať.\n\nProxMenux môže pridať pravidlá prístupu (ACL) k tomuto adresáru, ku všetkému v ňom a k tomu, čo sa v ňom vytvorí neskôr, pre používateľov a skupiny, pod ktorými {app} beží na hostiteľovi (ID {ids}). Vlastník súborov sa nemení a čokoľvek, čo ich dnes číta alebo zapisuje, to robí aj naďalej; vlastník adresára tiež získa prístup k súborom, ktoré {app} vytvorí.\n\nUdeliť {app} prístup k tomuto adresáru?", "{path} already exists and belongs to another user of the host, so {app} cannot write to it.\n\nProxMenux can add an access rule (ACL) for the identity {app} uses on the host (UID {uid}). Nothing that is there changes owner or permissions: whatever reads or writes those files today keeps doing so, and the owner of the directory also gets access to the files {app} creates.\n\nGrant {app} access to this directory?": "{path} už existuje a patrí inému používateľovi hostiteľa, takže {app} naň nemôže zapisovať.\n\nProxMenux môže pridať pravidlo prístupu (ACL) pre identitu, ktorú {app} používa na hostiteľovi (UID {uid}).Nič z toho, čo tam je, nemení vlastníka ani oprávnenia: čokoľvek, čo dnes číta alebo zapisuje tieto súbory, to robí aj naďalej a vlastník adresára tiež získa prístup k súborom, ktoré {app} vytvorí.\n\nUdeliť {app} prístup k tomuto adresáru?", - "{registry} answered with plain HTTP. Use a registry with HTTPS; TLS verification remains enabled.": "{registry} odpovedal jednoduchým HTTP. Použite register s HTTPS; Overenie TLS zostáva povolené.", + "{registry} answered with plain HTTP. Use a registry with HTTPS; TLS verification remains enabled.": "Register {registry} odpovedal nešifrovaným HTTP. Použite register s HTTPS. Overovanie TLS zostáva zapnuté.", "{registry} did not respond within {timeout} seconds. Try again later.": "{registry} neodpovedal do {timeout} sekúnd. Skúste to znova neskôr.", "{registry} is rate limiting requests. Wait a while and try again.": "Register {registry} obmedzuje počet požiadaviek. Chvíľu počkajte a skúste to znova.", "{registry} refused access to this image:\n{reference}\n\nIf the image is private, sign in with an account that can pull it.\n\nSign in now?": "Register {registry} odmietol prístup k tomuto obrazu:\n{reference}\n\nAk je obraz súkromný, prihláste sa účtom, ktorý má oprávnenie stiahnuť ho.\n\nPrihlásiť sa teraz?", diff --git a/oci/src/proxmenux_oci/cli.py b/oci/src/proxmenux_oci/cli.py index 3b611be9..7199a4dd 100644 --- a/oci/src/proxmenux_oci/cli.py +++ b/oci/src/proxmenux_oci/cli.py @@ -15,7 +15,7 @@ from . import console, images from .catalog import Catalog from .converter import ConversionError from .github_source import SourceError -from .i18n import N_, source_text, translate +from .i18n import N_, diagnostic, source_text, translate from .installer import ( ADDRESS_SHOWN, ADVANCED_MODE, @@ -38,7 +38,7 @@ PUBLISHERS = {"linuxserver.io": "LinuxServer", "official": N_("Official image")} STACK_LABELS = { "server": N_("Server"), "application": N_("Application"), - "machine_learning": "Machine learning", + "machine_learning": N_("Machine learning"), "database": "PostgreSQL", "valkey": "Valkey", "cache": "Redis", @@ -133,7 +133,7 @@ def _service_kind(service: dict[str, Any]) -> str: if service.get("is_main"): return translate("Application") if "machine-learning" in name or "machine-learning" in image: - return "Machine learning" + return translate("Machine learning") for key, label in SERVICE_KINDS: if key in image: return label @@ -267,7 +267,7 @@ def _deployment_summary_text(template: dict[str, Any], deployment: dict[str, Any target = (f"{mount['size_gb']} GB {on} {mount['source']}" if mount["type"] == "managed-volume" else mount["source"]) lines.append(f" {mount['container_path']} → {target}") - for key, label in (("media", "Library"), ("application", "Application data"), ("transfer", "Consume/export")): + for key, label in (("media", N_("Library")), ("application", N_("Application data")), ("transfer", N_("Consume/export"))): storage = plan.get(key) if isinstance(storage, dict) and "mode" in storage: if storage["mode"] == "host-bind": @@ -614,7 +614,7 @@ def interactive(catalog: Catalog) -> int: continue except (ConversionError, InstallError, OSError, ValueError) as exc: console.stop_spinner() - ui.message(f"{translate('The operation could not be completed')}:\n\n{exc}") + ui.message(f"{translate('The operation could not be completed')}:\n\n{diagnostic(exc)}") # ---------------------------------------------------------------- maintenance CLI @@ -773,7 +773,7 @@ def main(argv: list[str] | None = None) -> int: return 130 except (ConversionError, SourceError, InstallError, OSError, ValueError) as exc: console.stop_spinner() - print(f"ERROR: {exc}", file=sys.stderr) + print(f"ERROR: {diagnostic(exc)}", file=sys.stderr) return 1 diff --git a/oci/src/proxmenux_oci/i18n.py b/oci/src/proxmenux_oci/i18n.py index 2d39be2e..4efa371a 100644 --- a/oci/src/proxmenux_oci/i18n.py +++ b/oci/src/proxmenux_oci/i18n.py @@ -7,6 +7,7 @@ is the source language: a missing translation returns the English text. from __future__ import annotations import json +import re from pathlib import Path from typing import Any @@ -46,6 +47,23 @@ def translate(text: str) -> str: return _cache.get(text) or text +def diagnostic(error: Any) -> str: + """Translate legacy diagnostics only when presenting them to a person. + + The catalog's exception types and original messages remain stable for + internal callers; identifiers are inserted after the translation lookup. + """ + text = str(error) + for pattern, source in ( + (r"No template is available for '(.*)'", N_("No template is available for '{app_id}'")), + (r"The application '(.*)' is not in the index", N_("The application '{app_id}' is not in the index")), + ): + match = re.fullmatch(pattern, text) + if match: + return translate(source).format(app_id=match[1]) + return text + + def source_text(value: Any) -> str: """The English a catalog field was written in. diff --git a/oci/src/proxmenux_oci/stack.py b/oci/src/proxmenux_oci/stack.py index 442c1744..3b878d3f 100644 --- a/oci/src/proxmenux_oci/stack.py +++ b/oci/src/proxmenux_oci/stack.py @@ -365,13 +365,13 @@ def build_stack(template, ui, mode='advanced'): service_name = group['service'] if service_name not in envs: raise StackError(f"{translate('Unknown credential service:')} {service_name}") - if not quiet.confirm(f"{translate('Configure')} {group['label']} ({translate('optional')})", False): + if not quiet.confirm(f"{translate('Configure')} {translate(group['label'])} ({translate('optional')})", False): continue for field in group['fields']: if field['name'] not in envs[service_name] or envs[service_name][field['name']] != '': raise StackError(f"{translate('External field not reserved:')} {field['name']}") - value = (ui.password(field['label'], required=True) if field.get('sensitive',True) - else ui.ask(field['label'], required=True)) + value = (ui.password(translate(field['label']), required=True) if field.get('sensitive',True) + else ui.ask(translate(field['label']), required=True)) if not value or any(c in value for c in '\r\n'): raise StackError(translate('External credential is empty or spans multiple lines')) envs[service_name][field['name']] = value diff --git a/oci/tests/test_immich_acceleration.py b/oci/tests/test_immich_acceleration.py index 7a8448a2..737fc5c2 100644 --- a/oci/tests/test_immich_acceleration.py +++ b/oci/tests/test_immich_acceleration.py @@ -3,6 +3,7 @@ transcoding and its recognition: the CPU, or each usable GPU of the host for both or for only one of them.""" from pathlib import Path +import json import sys import unittest from unittest.mock import patch @@ -165,7 +166,15 @@ class ImmichAccelerationTests(unittest.TestCase): def test_the_name_of_the_machine_learning_container_is_not_translated(self, *_): script = (ROOT / "remote/install_immich_stack.sh").read_text() self.assertNotIn('translate "Machine learning"', script) - self.assertNotIn('translate("Machine learning")', (ROOT / "src/proxmenux_oci/cli.py").read_text()) + from proxmenux_oci.cli import _service_kind + from proxmenux_oci import i18n + service = {'name': 'immich-machine-learning', 'image': 'ghcr.io/immich-app/immich-machine-learning:release'} + original = dict(service) + with patch.object(i18n, 'language', return_value='sk'), patch.object(i18n, '_cache', json.loads((ROOT.parent / 'lang/sk.json').read_text())): + self.assertEqual(_service_kind(service), 'Strojové učenie') + _, _, plan = self.plan(NOTHING, DEFAULT_MODE) + self.assertIn('machine_learning', plan) + self.assertEqual(service, original) if __name__ == "__main__": From 6c056881969c0723bdc2421f4b6c66aa3e0dcd94 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:03:51 +0000 Subject: [PATCH 22/27] i18n: complete OCI runtime and maintenance Slovak diagnostics --- .../tests/test_oci_complete_errors_sk.py | 268 ++++++++ lang/sk.json | 623 ++++++++++-------- oci/remote/configure_jellyfin_encoding.py | 4 +- oci/remote/oci_dhcp_lease.py | 6 +- oci/remote/oci_image_cache.py | 7 +- oci/remote/oci_immich_recognition.py | 2 +- oci/remote/oci_installation_state.py | 2 +- oci/remote/oci_restore_recovery.py | 4 +- oci/remote/oci_rocm_check.py | 4 +- oci/remote/oci_update_check.py | 3 +- oci/remote/oci_watchdog.py | 9 +- oci/remote/rclone_mount_publish.py | 39 +- oci/remote/verify_oci_archive.py | 2 +- oci/src/proxmenux_oci/cli.py | 65 +- oci/src/proxmenux_oci/i18n.py | 45 ++ 15 files changed, 746 insertions(+), 337 deletions(-) create mode 100644 .github/scripts/tests/test_oci_complete_errors_sk.py diff --git a/.github/scripts/tests/test_oci_complete_errors_sk.py b/.github/scripts/tests/test_oci_complete_errors_sk.py new file mode 100644 index 00000000..8b54d976 --- /dev/null +++ b/.github/scripts/tests/test_oci_complete_errors_sk.py @@ -0,0 +1,268 @@ +"""Human diagnostics translate; IDs, JSON, error types and forbidden effects do not.""" +from contextlib import ExitStack, nullcontext, redirect_stderr, redirect_stdout +import copy +import importlib.util +import io +import json +from pathlib import Path +import sys +import tempfile +import unittest +from unittest.mock import MagicMock, Mock, patch +import urllib.error + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +import test_oci_complete_ui_sk as fixtures +from proxmenux_oci import casaos, cli, converter, i18n +from proxmenux_oci.catalog import Catalog +from proxmenux_oci.github_source import GitHubSource, SourceError +import oci_image_cache +import oci_dhcp_lease +import oci_rocm_check +import oci_ui +import oci_watchdog +import configure_jellyfin_encoding + + +class TechnicalMessages(unittest.TestCase): + setUp = fixtures.CompleteUI.setUp + patch = fixtures.CompleteUI.patch + + def remote_language(self): + self.patch(oci_ui, '_language', 'sk') + self.patch(oci_ui, '_cache', self.sk) + + def test_real_readme_errors_translate_only_at_the_display_boundary(self): + for text, expected in [('# App', 'README neobsahuje'), + ('## docker-compose\nplain text', 'Sekcia docker-compose neobsahuje blok kódu'), + ('## docker-compose\n```yaml\nservices: {}', 'Blok docker-compose nie je ukončený'), + ('## docker-compose\n```yaml\nnot-compose\n```', 'Nájdený blok nevyzerá')]: + with self.subTest(text=text), self.assertRaises(converter.ConversionError) as caught: + converter.extract_compose(text) + raw = str(caught.exception) + self.assertIn(expected, i18n.diagnostic(caught.exception)) + self.assertEqual(str(caught.exception), raw) + + def test_schema_error_keeps_field_details_and_original_exception(self): + with self.assertRaises(converter.ConversionError) as caught: + self.catalog.validate({}) + raw = str(caught.exception) + self.assertTrue(raw.startswith('La plantilla generada no cumple el esquema:')) + self.assertEqual(i18n.diagnostic(caught.exception), 'Vygenerovaná šablóna nezodpovedá schéme:' + raw.split(':', 1)[1]) + + def test_real_casaos_error_preserves_the_protocol_and_localizes_the_message(self): + with self.assertRaises(converter.ConversionError) as caught: + casaos.parse_casaos_compose('[]') + self.assertEqual(str(caught.exception), 'The CasaOS document is not a Compose mapping') + self.assertEqual(i18n.diagnostic(caught.exception), 'Dokument Compose musí byť mapa kľúčov a hodnôt.') + + def test_github_rate_limit_keeps_url_and_token_variable_without_a_network_call(self): + url = 'https://fiction.invalid/catalogue?ref=example' + error = urllib.error.HTTPError(url, 403, 'Forbidden', {'X-RateLimit-Remaining': '0'}, None) + request = self.patch(__import__('urllib.request', fromlist=['urlopen']), 'urlopen', side_effect=error) + with self.assertRaises(SourceError) as caught: + GitHubSource(token='fictional-token')._request(url) + request.assert_called_once() + text = i18n.diagnostic(caught.exception) + self.assertIn('GitHub vrátil HTTP 403', text) + self.assertIn(url, text) + self.assertIn('GITHUB_TOKEN', text) + self.assertNotIn('fictional-token', text) + + def test_github_timeout_retains_external_reason_and_three_attempts(self): + request = self.patch(__import__('urllib.request', fromlist=['urlopen']), 'urlopen', + side_effect=urllib.error.URLError('fictional external transport reason')) + self.patch(__import__('time'), 'sleep') + with self.assertRaises(SourceError) as caught: + GitHubSource()._request('https://fiction.invalid/template') + self.assertEqual(request.call_count, 3) + self.assertIn('ani po 3 pokusoch: fictional external transport reason', i18n.diagnostic(caught.exception)) + + def test_unknown_diagnostics_and_machine_codes_pass_through_unchanged(self): + for value in ['service:db:healthcheck-format', 'external tool: failure at 0x1234', + 'Check failed', 'Invalid cache', 'Ambiguous application', 'private-operation-id']: + self.assertEqual(i18n.diagnostic(value), value) + + def test_cli_help_preserves_flags_actions_defaults_and_numeric_types(self): + argv = ['manage', '104', '--action', 'recreate', '--unattended', '--min-image-age-days', '7'] + slovak = cli.build_parser() + self.assertIn('Vypísať aplikácie', slovak.format_help()) + with patch.object(i18n, '_language', 'en'): + english = cli.build_parser() + self.assertIn('List applications', english.format_help()) + self.assertEqual(vars(english.parse_args(argv)), vars(slovak.parse_args(argv))) + + def test_cli_summary_preserves_image_reference_ports_mount_paths_and_status_code(self): + template = self.catalog.compose('tandoor') + saved = copy.deepcopy(template) + text = cli._template_summary_text(template) + self.assertIn('Obraz: ' + template['container_contract']['image']['reference'], text) + self.assertIn('Dátové zväzky:', text) + self.assertIn('Stav: ' + template['status'], text) + for mount in template['container_contract']['volumes']: + self.assertIn(mount['container_path'], text) + self.assertEqual(template, saved) + + def test_install_cli_no_stays_successful_cancellation_without_dispatch(self): + template = self.catalog.compose('jellyfin') + saved = copy.deepcopy(template) + self.patch(cli.Catalog, 'compose', return_value=template) + self.patch(cli, 'build_deployment', return_value={'fiction': True}) + self.patch(cli, '_deployment_summary_text', return_value='Fiktívny plán') + answer = self.patch(__import__('builtins'), 'input', return_value='n') + output = io.StringIO() + with redirect_stdout(output): self.assertEqual(cli.main(['install', 'jellyfin']), 0) + self.assertIn('Nainštalovať? [y/N]:', answer.call_args.args[0]) + self.assertEqual(output.getvalue().count('Inštalácia bola zrušená.'), 1) + self.assertEqual(template, saved) + + def test_blocked_install_remains_error_one_and_never_asks_to_install(self): + template = self.catalog.compose('jellyfin') + template['compatibility'].update(automatic_install_candidate=False, untranslated_blockers=['fiction-blocker-code']) + self.patch(cli.Catalog, 'compose', return_value=template) + answer = self.patch(__import__('builtins'), 'input', side_effect=AssertionError('blocked installation asked a question')) + output = io.StringIO() + with redirect_stdout(io.StringIO()), redirect_stderr(output): + self.assertEqual(cli.main(['install', 'jellyfin']), 1) + self.assertIn('Inštalácia je zablokovaná: fiction-blocker-code', output.getvalue()) + answer.assert_not_called() + + def test_watchdog_manual_stop_stays_stopped_without_start_or_notice(self): + self.remote_language() + self.patch(oci_watchdog, 'watched', return_value={777: 'Fiction'}) + self.patch(oci_watchdog, 'is_running', return_value=False) + self.patch(oci_watchdog, 'is_busy', return_value=False) + self.patch(oci_watchdog, 'read_tasks', return_value=[('vzshutdown', '777', 75)]) + start = self.patch(oci_watchdog, 'start', side_effect=AssertionError('manual stop was undone')) + notice = self.patch(oci_watchdog.oci_operation_notice, 'notify') + state = oci_watchdog.new_state(); state['seen'] = 50 + output = io.StringIO() + with redirect_stdout(output): oci_watchdog.look({777: state}, 100) + self.assertTrue(state['settled']) + self.assertEqual(output.getvalue(), '') + start.assert_not_called(); notice.assert_not_called() + + def test_watchdog_localization_does_not_change_retry_state_or_notification_payload(self): + self.patch(oci_watchdog, 'watched', return_value={777: 'Fiction'}) + self.patch(oci_watchdog, 'is_running', return_value=False) + self.patch(oci_watchdog, 'is_busy', return_value=False) + self.patch(oci_watchdog, 'read_tasks', return_value=[]) + start = self.patch(oci_watchdog, 'start', return_value=True) + notice = self.patch(oci_watchdog.oci_operation_notice, 'notify') + outcomes = [] + for locale in ('en', 'sk'): + with patch.object(oci_ui, '_language', locale), patch.object(oci_ui, '_cache', self.sk): + state = oci_watchdog.new_state(); state['seen'] = 50 + text = io.StringIO() + with redirect_stdout(text): oci_watchdog.look({777: state}, 100) + outcomes.append((copy.deepcopy(state), notice.call_args, start.call_args)) + self.assertIn('CT 777 (Fiction)', text.getvalue()) + if locale == 'sk': self.assertIn('spúšťa sa znova (pokus 1)', text.getvalue()) + self.assertEqual(outcomes[0], outcomes[1]) + + def test_image_cache_json_contract_and_invalid_command_have_no_mutations(self): + self.remote_language() + self.patch(oci_image_cache.instances, 'locked', side_effect=lambda *a: nullcontext()) + self.patch(oci_image_cache, 'archives_of', return_value={}) + output = io.StringIO() + with redirect_stdout(output): self.assertEqual(oci_image_cache.main(['list', '777']), 0) + self.assertEqual(json.loads(output.getvalue()), {'archives': [], 'freed': 0}) + error = io.StringIO() + with redirect_stderr(error): self.assertEqual(oci_image_cache.main(['invalid']), 2) + self.assertIn('Použitie: oci_image_cache.py [prune | list VMID... | remove VMID...]', error.getvalue()) + + def test_rocm_timeout_localizes_but_external_gpu_errors_are_preserved(self): + self.remote_language() + self.patch(sys, 'argv', ['oci_rocm_check.py', '777']) + for raw, expected in [('the GPU did not answer in time', 'GPU neodpovedalo včas'), + ('external provider exception 0x123', 'external provider exception 0x123')]: + with self.subTest(raw=raw), patch.object(oci_rocm_check, 'check', side_effect=RuntimeError(raw)): + output = io.StringIO() + with redirect_stderr(output): self.assertEqual(oci_rocm_check.main(), 1) + self.assertEqual(output.getvalue().strip(), expected) + + def test_jellyfin_completion_distinguishes_changed_from_already_applied(self): + self.remote_language() + self.patch(sys, 'argv', ['configure_jellyfin_encoding.py', '/fiction-root', '{}']) + for changed, expected in [(True, 'Nastavenia Jellyfin boli aktualizované:'), + (False, 'Nastavenia Jellyfin už boli použité:')]: + with self.subTest(changed=changed), patch.object(configure_jellyfin_encoding, 'update_encoding', + return_value=(Path('/fiction-root/config/encoding.xml'), changed)): + output = io.StringIO() + with redirect_stdout(output): configure_jellyfin_encoding.main() + self.assertEqual(output.getvalue().strip(), expected + ' /config/encoding.xml') + + def test_dhcp_completion_keeps_interface_and_ip_version_without_repairing_a_guest(self): + self.remote_language() + self.patch(oci_dhcp_lease, 'LOCKS', MagicMock()) + self.patch(oci_dhcp_lease.fcntl, 'flock') + repair = self.patch(oci_dhcp_lease, 'repair', return_value=[('eth0', 4)]) + output = io.StringIO() + with redirect_stdout(output): oci_dhcp_lease.check(777, 0) + repair.assert_called_once_with(777, 0) + self.assertEqual(output.getvalue().strip(), 'CT 777: klient DHCP rozhrania eth0 (IPv4) bol znova spustený') + + +class StandalonePublisher(unittest.TestCase): + def test_mount_error_keeps_host_language_after_entering_the_container_root(self): + spec = importlib.util.spec_from_file_location('fiction_namespace_publisher', + fixtures.ROOT / 'oci/remote/rclone_mount_publish.py') + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + inside = False + def chroot(path): + nonlocal inside + inside = True + def read(path, **kwargs): + if inside: + raise FileNotFoundError('Host language catalog is outside the container root') + return ('{"language":"sk"}' if path.name == 'config.json' else + '{"clone source mount tree":"naklonovať strom zdrojového pripojenia"}') + libc = Mock(); libc.setns.return_value = 0; libc.syscall.return_value = -1 + with ExitStack() as guards: + for target, name, options in [ + (sys, 'argv', {'new': ['publisher.py', '777', '/fiction-source', '/fiction-target', 'ro']}), + (module.platform, 'machine', {'return_value': 'x86_64'}), + (Path, 'read_text', {'autospec': True, 'side_effect': read}), + (module.ctypes, 'CDLL', {'return_value': libc}), + (module.ctypes, 'get_errno', {'return_value': 13}), + (module.os, 'open', {'return_value': 42}), + (module.os, 'close', {}), (module.os, 'fchdir', {}), + (module.os, 'chroot', {'side_effect': chroot}), (module.os, 'chdir', {})]: + guards.enter_context(patch.object(target, name, **options)) + with self.assertRaises(OSError) as caught: + module.main() + self.assertTrue(inside) + self.assertEqual(caught.exception.errno, 13) + self.assertIn('naklonovať strom zdrojového pripojenia', str(caught.exception)) + self.assertEqual(libc.syscall.call_args.args[0], module.SYSCALLS['x86_64'][0]) + + def test_copied_helper_has_localized_errors_without_engine_import_or_mount_syscalls(self): + with tempfile.TemporaryDirectory(prefix='oci-publisher-language-') as directory: + root = Path(directory); (root / 'lang').mkdir() + (root / 'config.json').write_text('{"language":"sk"}') + (root / 'lang/sk.json').write_text(json.dumps({'Usage:': 'Použitie:', 'Unsupported host architecture:': 'Nepodporovaná architektúra hostiteľa:'})) + source = fixtures.ROOT / 'oci/remote/rclone_mount_publish.py' + copied = root / 'publisher.py'; copied.write_bytes(source.read_bytes()) + spec = importlib.util.spec_from_file_location('fiction_standalone_publisher', copied) + module = importlib.util.module_from_spec(spec) + with patch.dict(sys.modules, {'oci_ui': None}), patch.dict(__import__('os').environ, {'PMX_BASE_DIR': str(root)}): + spec.loader.exec_module(module) + with patch.object(module.ctypes, 'CDLL', side_effect=AssertionError('mount syscall path entered')) as syscall: + with patch.object(sys, 'argv', ['publisher.py']), redirect_stderr(io.StringIO()) as output: + self.assertEqual(module.main(), 2) + self.assertIn('Použitie: publisher.py PID SOURCE TARGET rw|ro', output.getvalue()) + with patch.object(sys, 'argv', ['publisher.py', '1', '/fiction-source', '/fiction-target', 'ro']), \ + patch.object(module.platform, 'machine', return_value='fiction-architecture'), redirect_stderr(io.StringIO()) as output: + self.assertEqual(module.main(), 2) + self.assertIn('Nepodporovaná architektúra hostiteľa: fiction-architecture', output.getvalue()) + syscall.assert_not_called() + (root / 'config.json').write_text('invalid json') + module._messages.cache_clear() + self.assertEqual(module.translate('Usage:'), 'Usage:') + (root / 'config.json').write_text('{"language":"en"}') + module._messages.cache_clear() + self.assertEqual(module.translate('Usage:'), 'Usage:') + + +if __name__ == '__main__': unittest.main() diff --git a/lang/sk.json b/lang/sk.json index edd0df4f..55ceb340 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -1,4 +1,67 @@ { + "'auto'/'local', or root@IP for development": "'auto'/'local' alebo root@IP pri vývoji", + "Add the host firewall rule of this container; one option for each confirmed rule": "Pridať pravidlo firewallu hostiteľa pre tento kontajner; jedna voľba pre každé potvrdené pravidlo", + "Another OCI operation is using the instance registry.": "Register inštancií používa iná operácia OCI.", + "Blockers": "Prekážky", + "Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application": "Hromadné generovanie vyžaduje GITHUB_TOKEN na získanie nemennej revízie každej aplikácie", + "CT {vmid} ({name}) could not be kept running; it is left stopped": "CT {vmid} ({name}) sa nepodarilo udržať v chode; zostáva zastavený", + "CT {vmid} ({name}) stopped on its own; starting it again (attempt {attempt})": "CT {vmid} ({name}) sa sám zastavil; spúšťa sa znova (pokus {attempt})", + "CT {vmid}: DHCP client of {name} (IPv{version}) started again": "CT {vmid}: klient DHCP rozhrania {name} (IPv{version}) bol znova spustený", + "Configure and install an application": "Nastaviť a nainštalovať aplikáciu", + "Could not reach {url} after 3 attempts: {reason}": "Adresa {url} nie je dostupná ani po 3 pokusoch: {reason}", + "Emit numeric progress for the OCI menu": "Vypisovať číselný priebeh pre ponuku OCI", + "Enable a mount on an installed Rclone OCI": "Nastaviť pripojenie v nainštalovanom OCI kontajneri Rclone", + "Generated: {generated}; failed: {failed}; family: {provider}": "Vygenerované: {generated}; neúspešné: {failed}; rodina: {provider}", + "GitHub returned HTTP 403 for {url}. Set GITHUB_TOKEN to raise the API rate limit.": "GitHub vrátil HTTP 403 pre {url}. Nastavením GITHUB_TOKEN zvýšite limit požiadaviek API.", + "GitHub returned HTTP {status} for {url}.": "GitHub vrátil HTTP {status} pre {url}.", + "GitHub returned a truncated tree for the CasaOS catalogue": "GitHub vrátil neúplný strom súborov katalógu CasaOS", + "Host directories are not reverted by the backup (confirmed beforehand)": "Adresáre hostiteľa sa zo zálohy nevracajú do pôvodného stavu (vopred potvrdené)", + "Icons resolved: {resolved}; cleared: {cleared}; unchanged: {unchanged}; with a theme variant: {themed}": "Priradené ikony: {resolved}; vymazané: {cleared}; nezmenené: {unchanged}; s variantom témy: {themed}", + "Index updated: {count} candidate applications": "Index bol aktualizovaný: {count} kandidátnych aplikácií", + "Install? [y/N]:": "Nainštalovať? [y/N]:", + "Installation blocked:": "Inštalácia je zablokovaná:", + "Installation cancelled.": "Inštalácia bola zrušená.", + "Internal installer operation; not legacy adoption": "Interná operácia inštalátora; neslúži na prevzatie existujúceho kontajnera", + "Jellyfin configuration already applied:": "Nastavenia Jellyfin už boli použité:", + "Jellyfin configuration updated:": "Nastavenia Jellyfin boli aktualizované:", + "Keep the backup taken before the update in this Proxmox storage": "Ponechať zálohu vytvorenú pred aktualizáciou v tomto úložisku Proxmox", + "List applications": "Vypísať aplikácie", + "No Compose files were found in the CasaOS catalogue": "V katalógu CasaOS sa nenašli žiadne súbory Compose", + "Only the applications that need no question: the ones the cluster keeps a record of": "Iba aplikácie bez potreby otázok: tie, ktorých záznam uchováva klaster", + "Open the page of an application in the menu": "Otvoriť stránku aplikácie v ponuke", + "ProxMenux OCI manager Apps (Technology Preview)": "ProxMenux – správca OCI aplikácií (Technology Preview)", + "Rclone remote": "Vzdialené úložisko Rclone", + "Refresh the index from the image sources": "Obnoviť index zo zdrojov obrazov", + "Regenerate the catalog templates": "Znova vygenerovať šablóny katalógu", + "Regenerate the template of one application": "Znova vygenerovať šablónu jednej aplikácie", + "Register again the OCI applications restored from a backup": "Znova zaregistrovať OCI aplikácie obnovené zo zálohy", + "Resolve catalog icons against the jsdelivr icon sets": "Priradiť ikony katalógu zo súprav ikon jsdelivr", + "Scheduled run: no questions; stops where a person has to decide": "Plánovaný beh bez otázok; zastaví sa pri potrebe rozhodnutia používateľa", + "Show the summary of a template": "Zobraziť súhrn šablóny", + "The ProxMenux application catalogue is not a list": "Katalóg aplikácií ProxMenux nemá formát zoznamu", + "The README has no recognisable docker-compose section": "README neobsahuje rozpoznateľnú sekciu docker-compose", + "The application '{app_id}' does not come from LinuxServer": "Aplikácia '{app_id}' nepochádza z LinuxServer", + "The block found does not look like a Docker Compose file": "Nájdený blok nevyzerá ako súbor Docker Compose", + "The docker-compose block is not closed": "Blok docker-compose nie je ukončený", + "The docker-compose section contains no code block": "Sekcia docker-compose neobsahuje blok kódu", + "The main Compose service cannot be identified unambiguously": "Hlavnú službu v súbore Compose nemožno jednoznačne určiť", + "Unsupported host architecture:": "Nepodporovaná architektúra hostiteľa:", + "Update, modify or recreate one installed OCI instance": "Aktualizovať, upraviť alebo znova vytvoriť jednu nainštalovanú inštanciu OCI", + "Volumes": "Dátové zväzky", + "clone source mount tree": "klonovanie zdrojového stromu pripojenia", + "enter container namespace": "vstup do menného priestoru kontajnera", + "installable": "možno nainštalovať", + "main container of the application": "hlavný kontajner aplikácie", + "make cloned mount tree read-only": "nastavenie klonovaného stromu pripojenia iba na čítanie", + "none": "žiadne", + "not generated": "nevygenerované", + "pending adaptation": "čaká na prispôsobenie", + "print the estimated unpacked size in MiB (0 when unknown)": "vypísať odhad rozbalenej veľkosti v MiB (0, ak nie je známa)", + "publish mount tree": "sprístupnenie stromu pripojenia", + "python3-jsonschema is required to generate templates": "Na generovanie šablón je potrebný python3-jsonschema", + "removed unused image archive: {path} ({size} bytes)": "odstránený nepoužívaný archív obrazu: {path} ({size} bajtov)", + "return to host namespace": "návrat do menného priestoru hostiteľa", + "the GPU did not answer in time": "GPU neodpovedalo včas", "Application data": "Údaje aplikácie", "Consume/export": "Priečinky consume a export", "Library": "Knižnica", @@ -52,12 +115,12 @@ "A backup was modified": "Záloha bola zmenená", "A command did not finish in time:": "Príkaz neskončil včas:", "A complete restore will:": "Úplná obnova vykoná:", - "A concurrent change was detected; the container is not removed": "Bola zistená súbežná zmena; obal sa neodstraňuje", - "A container mount has a source, backup or permission different from the saved record": "Montáž kontajnera má zdroj, zálohu alebo povolenie odlišné od uloženého záznamu", + "A concurrent change was detected; the container is not removed": "Bola zistená súbežná zmena; kontajner sa neodstráni", + "A container mount has a source, backup or permission different from the saved record": "Pripojenie v kontajneri má iný zdroj, nastavenie zálohovania alebo oprávnenia než uložený záznam", "A container without its record stays as an ordinary container and is not offered again.": "Kontajner bez záznamu zostane ako obyčajný kontajner a už sa neponúka.", "A coordinated operation has a saved journal. Continuing attempts to recover the previous stack where needed, or finish cleanup for a completed operation. Recovery or cleanup can fail.": "Koordinovaná operácia má uložený záznam priebehu. Pokračovanie sa podľa potreby pokúsi obnoviť predchádzajúci stav aplikácie alebo dokončiť upratovanie už dokončenej operácie. Obnova aj upratovanie môžu zlyhať.", "A coordinated operation is pending. The whole previous stack will be recovered, not only the selected member. If the operation already finished, the cleanup of its markers is completed.": "Koordinovaná operation čaká. Celý predchádzajúci stack bude obnovený, nielen vybraný člen. Ak už operation skončil, vyčistenie jeho markerov je ukončené.", - "A different host monitor include already exists; it is not overwritten:": "Iný hostiteľský monitor už existuje; nie je prepísaný:", + "A different host monitor include already exists; it is not overwritten:": "Už existuje iný pripojený konfiguračný súbor monitora hostiteľa; neprepíše sa:", "A fancy monitoring tool": "Nádherný monitorovací nástroj", "A free and open-source cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with schemata.": "MongoDB je bezplatná multiplatformová dokumentová databáza s otvoreným zdrojovým kódom. Patrí medzi databázy NoSQL a používa dokumenty podobné JSON so schémami.", "A free reverse proxy for tunneling services (not self-hosted).": "Bezplatný reverzný proxy na tunelovanie služieb (neprevádzkuje sa na vlastnom serveri).", @@ -75,7 +138,7 @@ "A keyfile is installed at:": "Keyfile je nainštalovaný v:", "A legacy gasket-dkms package was found on this host, but no Coral M.2 / PCIe hardware is present.": "Na tomto hostiteľovi sa našiel starý balík gasket-dkms, ale nie je prítomný žiadny hardvér Coral M.2 / PCIe.", "A managed rootfs and an unprivileged container are required": "Riadené rootfs a neprivilegovaný kontajner sú required", - "A managed volume with backup enabled is required": "Riadený objem so zapnutou zálohou je required", + "A managed volume with backup enabled is required": "Vyžaduje sa spravovaný dátový zväzok so zapnutým zálohovaním", "A matching managed host firewall rule may also be removed.": "Môže sa odstrániť aj zodpovedajúce spravované pravidlo firewallu hostiteľa.", "A member VMID is in use by another guest or is on another node": "VMID člena aplikácie používa iný hosť alebo je kontajner na inom uzle", "A member configuration changed after the stack was checked": "Nastavenie kontajnera sa po kontrole aplikácie zmenilo", @@ -94,7 +157,7 @@ "A newer kernel is installed. Reboot before the next backup window to use it.": "Je nainštalované novšie jadro. Aby ste ho mohli použiť, reštartujte ho pred ďalším oknom zálohy.", "A newer version is available:": "Je dostupná novšia verzia:", "A pending operation exists for": "Nedokončená operácia existuje pre", - "A pending stack assembly already exists; it is not overwritten": "Nevyriešená zostava stohov už existuje; nie je prepísaná", + "A pending stack assembly already exists; it is not overwritten": "Už existuje nedokončené zostavenie aplikácie; neprepíše sa", "A previous NVIDIA refresh is pending review": "Predchádzajúce obnovovanie NVIDIA čaká na preskúmanie", "A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "Pre nasledujúce GPU NVIDIA bola zistená predchádzajúca konfigurácia priechodu VFIO:", "A privacy-first, open-source platform for knowledge management and collaboration.": "Platforma na správu znalostí a spoluprácu s otvoreným zdrojovým kódom a dôrazom na súkromie.", @@ -105,8 +168,8 @@ "A reboot is required to finish the restore.": "Na dokončenie obnovy je potrebný reštart.", "A reboot will be required to complete the restore.": "Na dokončenie obnovy bude potrebný reštart.", "A relaxed AppArmor or seccomp profile is requested; this may be optional.": "Požaduje sa uvoľnený profil AppArmor alebo seccomp; môže byť voliteľný.", - "A reproducible native startup is missing": "Chýba reprodukovateľný prirodzený štart", - "A rootfs adaptation is stored in persistent storage": "Adaptácia korienkov sa skladuje v trvalom sklade", + "A reproducible native startup is missing": "Chýba natívny spôsob spustenia, ktorý možno zopakovať", + "A rootfs adaptation is stored in persistent storage": "Úprava koreňového súborového systému je uložená v trvalom úložisku", "A running container is restarted to apply the changes; a stopped one is left stopped. The data of the application, its database and the configuration of the other containers are not changed. A container volume removed here is deleted with the data in it.": "Spustený kontajner sa reštartuje, aby sa zmeny použili; zastavený zostane zastavený. Údaje aplikácie sa zachovajú. Databáza ani ostatné kontajnery sa nemenia. Odstránením trvalého úložiska kontajnera sa vymažú aj údaje, ktoré obsahuje.", "A scheduled run cannot answer:": "Plánované spustenie nemôže odpovedať:", "A self-hosted Bitwarden server": "Server Bitwarden na vlastnom serveri", @@ -118,14 +181,14 @@ "A simple, open-source file sharing host.": "Jednoduchá služba na zdieľanie súborov s otvoreným zdrojovým kódom.", "A simple, private file server.": "Jednoduchý súkromný súborový server.", "A single matching image platform cannot be resolved": "Jedinú zodpovedajúcu obrazovú platformu nie je možné vyriešiť", - "A single-platform OCI archive is required": "Jednoplatformový OCI archív je required", + "A single-platform OCI archive is required": "Vyžaduje sa archív OCI pre jedinú platformu", "A stack backup is missing; a partial restore is not allowed": "Chýba záloha jedného z kontajnerov aplikácie; čiastočná obnova nie je povolená", - "A stack member has a different identity": "Člen stohu má inú identitu", + "A stack member has a different identity": "Kontajner zostavy má inú identitu", "A system reboot is recommended to ensure all changes take effect.": "Odporúča sa reštart systému, aby sa všetky zmeny bezpečne prejavili.", "A third party companion app available to Plex server owners to allow their users to request, review and discover content.": "Aplikácia tretej strany pre majiteľov serverov Plex, ktorá umožňuje ich používateľom požadovať, skúmať a objavovať obsah.", "A third-party client for self-hosted server and self-hosted server, remote access management interface, remote access to installed applications.": "Klient tretej strany pre vlastný server s rozhraním na správu vzdialeného prístupu a prístup k nainštalovaným aplikáciám.", - "A tmpfs mount is not part of the journal; recovery blocked": "A tmpfs mount nie je súčasťou časopisu; zotavenie blokované", - "A tmpfs mount overlaps another mount": "Pripojenie tmpfs sa prekrýva s inou montážou", + "A tmpfs mount is not part of the journal; recovery blocked": "Pripojenie tmpfs nie je súčasťou záznamu operácie; obnova je zablokovaná", + "A tmpfs mount overlaps another mount": "Pripojenie tmpfs sa prekrýva s iným pripojením", "A tunneling daemon by Cloudflare that safely exposes your web servers into the internet.": "Tunelový démon Cloudflare, ktorý bezpečne vystavuje vaše webové servery na internet.", "A versatile file conversion tool that supports multiple formats.": "Univerzálny nástroj na konverziu súborov, ktorý podporuje viacero formátov.", "A web GUI client of Project V which supports VMess, VLESS, SS, SSR, Trojan, Tuic and Juicity protocols": "Webový GUI klient projektu V, ktorý podporuje protokoly VMess, VLESS, SS, SSR, Trojan, Tuic a Juicity", @@ -200,7 +263,7 @@ "Access could not be granted completely; the application may be unable to write to:": "Prístup nebolo možné úplne udeliť; aplikácia možno nebude môcť zapisovať do:", "Access could not be granted; the application cannot write to:": "Prístup nebolo možné udeliť; aplikácia nemôže písať na:", "Access denied by {registry}. Check the image name and sign in from Registry accounts.": "Register {registry} odmietol prístup. Skontrolujte názov obrazu a prihláste sa cez Účty registrov.", - "Access granted to the application on:": "Prístup udelený aplikácii dňa:", + "Access granted to the application on:": "Aplikácii bol udelený prístup k:", "Access password of the web interface (optional)": "Heslo na prístup do webového rozhrania (voliteľné)", "Access profile:": "Profil prístupu:", "Access token of the Jupyter Lab web interface": "Prístupový token webového rozhrania Jupyter Lab", @@ -382,8 +445,8 @@ "Also install the VirtIO network driver during setup to enable network access.": "Počas inštalácie nainštalujte aj sieťový ovládač VirtIO, aby fungoval prístup k sieti.", "Although VFIO can bind to this device, full passthrough to a VM is": "Aj keď sa VFIO vie na toto zariadenie naviazať, úplný passthrough do VM je", "Altus is an Electron-based WhatsApp client with themes and multiple account support.": "Altus je klient WhatsApp založený na Electrone s podporou tém a viacerých účtov.", - "Ambiguous mount points in the container": "Ambiciózne body upevnenia v kontajneri", - "Ambiguous or invalid environment variable": "Ambiciózna alebo neplatná premenná prostredia", + "Ambiguous mount points in the container": "Nejednoznačné body pripojenia v kontajneri", + "Ambiguous or invalid environment variable": "Nejednoznačná alebo neplatná premenná prostredia", "Amount of RAM in MiB (default: 4096)": "Množstvo RAM v MiB (predvolené: 4096)", "An AI model used to generate images conditioned on text descriptions.": "Model AI používaný na generovanie obrázkov podmienených textovými popismi.", "An AMD dedicated GPU has been detected without FLR support": "Bola zistená dedikovaná AMD GPU bez podpory FLR", @@ -393,11 +456,11 @@ "An Intel dedicated GPU has been detected without FLR support": "Bola zistená dedikovaná Intel GPU bez podpory FLR", "An accelerated video generation framework that speeds up end-to-end diffusion while preserving video quality": "Rámec na zrýchlené generovanie videa, ktorý urýchľuje celý difúzny proces pri zachovaní kvality videa", "An application changed since the update check. Check for updates again.": "Od poslednej kontroly sa aplikácia zmenila. Znova vyhľadajte aktualizácie.", - "An executable required by the adapter is missing in the new image": "V novom obrázku chýba spustiteľný requi.", + "An executable required by the adapter is missing in the new image": "V novom obraze chýba spustiteľný súbor vyžadovaný adaptérom", "An fstab entry already exists for:": "Záznam vo fstab už existuje pre:", "An image probe container was started externally": "Externe bola spustená obrazová sonda", - "An include is not part of the journal; recovery blocked": "Zahrnutie nie je súčasťou časopisu; obnova zablokovaná", - "An include was modified outside the journal; recovery blocked": "Zahrnutie bolo upravené mimo časopisu; regenerácia zablokovaná", + "An include is not part of the journal; recovery blocked": "Pripojený konfiguračný súbor nie je súčasťou záznamu operácie; obnova je zablokovaná", + "An include was modified outside the journal; recovery blocked": "Pripojený konfiguračný súbor bol zmenený mimo záznamu operácie; obnova je zablokovaná", "An open source generative AI development platform for building AI Agents and LLM workflows": "Vývojová platforma generatívnej AI s otvoreným zdrojovým kódom na tvorbu agentov AI a pracovných postupov s LLM", "An operation of this installation has not finished; recover it from the management menu before removing it": "Operácia tejto inštalácie sa ešte nedokončila; pred odstránením ju obnovte v ponuke správy OCI", "An update does not accept configuration changes": "Aktualizácia neprijíma zmeny konfigurácie", @@ -428,7 +491,7 @@ "Application responding:": "Aplikácia odpovedá:", "Application responding; checking its stability...": "Aplikácia reaguje; kontrola jej stability...", "Application suite: one independent LXC per selected application": "Zostava aplikácií: samostatný kontajner LXC pre každú vybranú aplikáciu", - "Application:": "Použitie:", + "Application:": "Aplikácia:", "Applications you can choose:": "Aplikácie si môžete vybrať:", "Apply": "Použiť", "Apply AMD CPU fixes": "Použiť opravy pre AMD procesory", @@ -538,7 +601,7 @@ "Automated post-installation script": "Automatický skript po inštalácii", "Automatic Video Library Manager for TV Shows. It watches for new episodes of your favorite shows, and when they are posted it does its magic.": "Automatický správca knižnice televíznych seriálov. Sleduje nové epizódy obľúbených seriálov a spracuje ich, keď sú dostupné.", "Automatic detection": "Automatická detekcia", - "Automatic private network allocation requires a /24 subnet": "Automatické pridelenie súkromných sietí requires a /24 subnet", + "Automatic private network allocation requires a /24 subnet": "Automatické pridelenie súkromnej siete vyžaduje podsieť /24", "Automatic video library manager for TV Shows": "Automatický správca knižnice televíznych seriálov", "Automatic/Unattended": "Automaticky/bez zásahu", "Automation & Scheduling": "Automatizácia a plánovanie", @@ -724,8 +787,8 @@ "CT": "CT", "CT started successfully.": "CT bol úspešne spustený.", "CT {vmid} is a host monitor and had a rule in the host firewall. Allow TCP port {port} from {subnet} through the firewall of this host? Existing firewall rules are not changed.": "CT {vmid} je hostiteľský monitor a mal pravidlo v hostiteľskom firewalle. Povoliť TCP port {port} z {subnet} cez bránu firewall tohto hostiteľa? Existujúce pravidlá brány firewall sa nemenia.", - "CUDA requires a working NVIDIA driver": "CUDA requires pracovný ovládač NVIDIA", - "CUDA requires the NVIDIA Container Toolkit on the host": "CUDA requires the NVIDIA Container Toolkit na hostiteľovi", + "CUDA requires a working NVIDIA driver": "CUDA vyžaduje funkčný ovládač NVIDIA", + "CUDA requires the NVIDIA Container Toolkit on the host": "CUDA vyžaduje NVIDIA Container Toolkit na hostiteľovi", "Calculate all kinds of statistics from your (local) Emby or Jellyfin server": "Vypočítajte všetky druhy štatistík z vášho (miestneho) servera Emby alebo Jellyfin", "Calibre is a powerful and easy to use e-book manager. Users say it's outstanding and a must-have. It'll allow you to do nearly everything and it takes things a step beyond normal e-book software. It's also completely free and open source and great for both casual users and computer experts.": "Calibre je výkonný a ľahko použiteľný správca elektronických kníh. Ponúka rozsiahle možnosti nad rámec bežných nástrojov a používatelia ho vysoko hodnotia. Je úplne bezplatný, s otvoreným zdrojovým kódom a vhodný pre bežných používateľov aj odborníkov.", "Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself.": "Calibre-web je webová aplikácia poskytujúca čisté rozhranie pre prehliadanie, čítanie a sťahovanie elektronických kníh pomocou existujúcej databázy Calibre. Je tiež možné integrovať Google disk a upraviť metaúdaje a knižnicu calibre prostredníctvom samotnej aplikácie.", @@ -741,7 +804,7 @@ "Cannot create:": "Nedá sa vytvoriť:", "Cannot detect filesystem on": "Nedá sa zistiť súborový systém na", "Cannot find": "Nedá sa nájsť", - "Cannot identify the vendor of the device:": "Nie je možné identifikovať predajcu zariadenia:", + "Cannot identify the vendor of the device:": "Nepodarilo sa určiť výrobcu zariadenia:", "Cannot inspect current swap configuration": "Nie je možné skontrolovať aktuálnu konfiguráciu výmeny", "Cannot inspect existing ZRAM configurations": "Nie je možné skontrolovať existujúce konfigurácie ZRAM", "Cannot inspect existing ZRAM generator configuration": "Nie je možné skontrolovať existujúcu konfiguráciu generátora ZRAM", @@ -758,7 +821,7 @@ "Cannot validate credentials - no shares available for testing.": "Prihlasovacie údaje sa nedajú overiť - nie sú dostupné žiadne zdieľania na test.", "Cannot verify the reused disk:": "Nie je možné overiť znovu použitý disk:", "Cannot write the image archive to {storage}. Check storage access and mount status.": "Archív obrazu sa nedá zapísať do úložiska {storage}. Skontrolujte prístup k úložisku a stav jeho pripojenia.", - "Capabilities cannot be kept and all dropped at the same time": "Nie je možné udržať si schopnosti a zároveň ich znížiť.", + "Capabilities cannot be kept and all dropped at the same time": "Oprávnenia capabilities nemožno súčasne ponechať aj všetky odobrať", "Catalog options": "Možnosti katalógu", "Category": "Kategória", "Caution: Maximum mode generates more heat.": "Pozor: maximálny režim vytvára viac tepla.", @@ -806,8 +869,8 @@ "Changing Release Channel": "Mením vetvu vydania", "Changing between a privileged and an unprivileged container, or the host monitor mode, requires installing the application again": "Pri prepnutí medzi privilegovaným a neprivilegovaným kontajnerom alebo pri zmene režimu monitorovania hostiteľa je aplikáciu potrebné nainštalovať znova.", "Changing the machine type on an existing installed VM is not safe: it changes the chipset and PCI slot layout, which typically prevents the guest OS from booting.": "Meniť typ stroja na už nainštalovanej VM nie je bezpečné: zmení sa chipset a rozloženie PCI slotov, čo často zabráni štartu hosťovského OS.", - "Changing the rootfs or its storage requires a separate migration": "Zmena rootfs alebo jeho skladovanie requires samostatnú migráciu", - "Changing the storage or size of a disk requires a migration; empty disks are not created": "Zmena úložiska alebo veľkosti disku requires migrácie; prázdne disky nie sú vytvorené", + "Changing the rootfs or its storage requires a separate migration": "Zmena koreňového súborového systému alebo jeho úložiska vyžaduje samostatnú migráciu", + "Changing the storage or size of a disk requires a migration; empty disks are not created": "Zmena úložiska alebo veľkosti disku vyžaduje migráciu; prázdne disky sa nevytvoria", "Check": "Skontrolujte", "Check BIOS/UEFI in Hardware > BIOS — must match what the original VM used": "Skontrolujte BIOS/UEFI v Hardware > BIOS - musí sedieť s tým, čo používala pôvodná VM", "Check Coral USB/M.2 detection": "Skontrolovať detekciu Coral USB/M.2", @@ -859,13 +922,13 @@ "Checking the container before the update...": "Kontrola kontajnera pred aktualizáciou...", "Checking the container...": "Kontroluje sa kontajner...", "Checking the device permissions for the application user...": "Kontrola oprávnení zariadenia pre užívateľa aplikácie...", - "Checking the image compatibility:": "Kontrola kompatibility obrázku:", + "Checking the image compatibility:": "Kontroluje sa kompatibilita obrazu:", "Checking the image in the registry...": "Kontrola obrazu v registri...", "Checking the interrupted operation...": "Kontrolujem prerušenú operáciu...", "Checking the interrupted stack operation...": "Kontrolujem prerušenú operáciu celej aplikácie...", "Checking the new image without starting it:": "Kontrolujem nový obraz bez jeho spustenia:", "Checking the recreated stack...": "Kontrolujem znovu vytvorené kontajnery aplikácie...", - "Checking the remote...": "Kontrolujem ovládač...", + "Checking the remote...": "Kontroluje sa vzdialené úložisko...", "Checking the restored installation": "Kontrola obnovenej inštalácie", "Checking the restored installation...": "Kontrola obnovenej inštalácie...", "Checking the stack before recreating...": "Kontrolujem aplikáciu pred opätovným vytvorením kontajnerov...", @@ -1019,7 +1082,7 @@ "Component was installed on the backup source but no matching hardware was found on this host.": "Súčasť bola nainštalovaná na zdrojovom hostovi zálohy, ale na tomto hostovi sa nenašiel zodpovedajúci hardvér.", "Component:": "Súčasť:", "Components to uninstall (manual for now):": "Súčasti na odinštalovanie (zatiaľ ručne):", - "Compose capabilities validated in the LXC user namespace:": "Skladanie schopností validovaných v LXC užívateľskom priestore:", + "Compose capabilities validated in the LXC user namespace:": "Oprávnenia capabilities zo súboru Compose boli overené v používateľskom mennom priestore LXC:", "Compose file of the application": "Súbor Compose aplikácie", "Compose file of this host": "súboru Compose z tohto hostiteľa", "Compressed size:": "Veľkosť po kompresii:", @@ -1175,7 +1238,7 @@ "Container Status": "Stav kontajnera", "Container checked": "Skontrolovaný kontajner", "Container configuration not found": "Nastavenie kontajnera sa nenašlo", - "Container configured (not started):": "Kontajner nastavený (nezačína sa):", + "Container configured (not started):": "Kontajner je nastavený (nie je spustený):", "Container converted to privileged": "Kontajner prevedený na privilegovaný", "Container created:": "Vytvorený kontajner:", "Container data targeted for deletion:": "Údaje kontajnera určené na vymazanie:", @@ -1187,11 +1250,11 @@ "Container is already stopped.": "Kontajner už je zastavený.", "Container is running. Restart to apply changes?": "Kontajner beží. Reštartovať ho, aby sa zmeny prejavili?", "Container is stopped. Start it now to verify the mount works?": "Kontajner je zastavený. Spustiť ho teraz a overiť, či mount funguje?", - "Container kept with its data; the installation was not validated:": "Kontajner uchovávaný so svojimi údajmi; zariadenie nebolo potvrdené:", + "Container kept with its data; the installation was not validated:": "Kontajner aj jeho údaje zostávajú zachované; inštalácia nebola overená:", "Container mount point:": "Mount point v kontajneri:", "Container must be stopped before conversion": "Kontajner musí byť pred prevodom zastavený", - "Container prepared for the stack:": "Nádoba pripravená na zásobník:", - "Container recreated": "Zrekonštruovaný kontajner", + "Container prepared for the stack:": "Kontajner pripravený pre zostavu:", + "Container recreated": "Kontajner bol znova vytvorený", "Container removed:": "Odstránený kontajner:", "Container restarted successfully": "Kontajner bol úspešne reštartovaný", "Container running steadily": "Kontajner beží plynule", @@ -1222,7 +1285,7 @@ "Containers that will be created (one LXC per service, on a private network):": "Kontajnery, ktoré budú vytvorené (jeden LXC na službu, na súkromnej sieti):", "Containers that will receive this device": "Kontajnery, ktoré dostanú toto zariadenie", "Containers that will receive this path": "Kontajnery, ktoré dostanú túto cestu", - "Containers:": "Obaly:", + "Containers:": "Kontajnery:", "Contains files": "Obsahuje súbory", "Contains:": "Obsahuje:", "Content Types": "Typy obsahu", @@ -1243,7 +1306,7 @@ "Continue: Proceed with conversion": "Pokračovať: spustiť prevod", "Continue?": "Pokračovať?", "Continuing with your selection.": "Pokračujem s vaším výberom.", - "Contradictory tmpfs options": "Možnosti kontrastných tmpfs", + "Contradictory tmpfs options": "Protichodné voľby tmpfs", "Controller": "Controller", "Controller + NVMe": "Controller + NVMe", "Controller + NVMe assignment will be written now and become active after host reboot.": "Priradenie Controller + NVMe sa teraz zapíše a aktivuje sa po reštarte servera.", @@ -1274,7 +1337,7 @@ "Converting image using command:": "Konvertujem obraz pomocou príkazu:", "Converting the container to privileged...": "Konverzia kontajnera na privilegovaný...", "Converts to deb822; keeps .list backups as .bak": "Prevedie na deb822; zálohy .list ponechá ako .bak", - "Coordinated backups require zstd": "Koordinované zálohy require zstd", + "Coordinated backups require zstd": "Koordinované zálohy vyžadujú zstd", "Cops by Sébastien Lucas, now maintained by MikesPub, stands for Calibre OPDS (and HTML) Php Server.": "COPS od Sébastiena Lucasa, dnes spravovaný MikesPub, znamená Calibre OPDS (a HTML) PHP Server.", "Copy a peer configuration to the host with: pct pull /config/peer1/peer1.conf peer1.conf": "Kopírovať konfiguráciu peer k hostiteľovi s: pct pull /config/peer1/peer1.conf peer1.conf", "Copy failed": "Kopírovanie zlyhalo", @@ -1311,18 +1374,18 @@ "Coral packages purged.": "Balíky Coral boli úplne odstránené.", "Coral uninstallation completed.": "Odinštalovanie Coral je hotové.", "Core Proxmox packages reinstalled successfully": "Základné balíky Proxmoxu boli úspešne preinštalované", - "Core is running, but not responding over HTTP on 80/8123": "Jadro beží, ale neodpovedá cez HTTP na 80/8123", - "Core is still on the initial installation page": "Jadro je stále na úvodnej inštalačnej stránke", + "Core is running, but not responding over HTTP on 80/8123": "Home Assistant Core beží, ale neodpovedá cez HTTP na porte 80 ani 8123", + "Core is still on the initial installation page": "Home Assistant Core je stále na úvodnej inštalačnej stránke", "Core packages": "Základné balíky", "Cores": "Počet jadier", - "Corrupted gzip layer": "Vrstvená vrstva gzipu", + "Corrupted gzip layer": "Poškodená vrstva gzip", "Could not add": "Nepodarilo sa pridať", "Could not add disk": "Disk sa nepodarilo pridať", "Could not add the confirmed host firewall rule": "Potvrdené pravidlo firewallu hostiteľa sa nepodarilo pridať", "Could not add the device to the container:": "Nepodarilo sa pridať zariadenie do kontajnera:", "Could not add the mount point:": "Nepodarilo sa pridať bod pripojenia:", "Could not apply the Compose extra hosts": "Nepodarilo sa použiť extra hostiteľov", - "Could not apply the Compose supplementary groups": "Nepodarilo sa použiť zostavu doplnkových skupín", + "Could not apply the Compose supplementary groups": "Nepodarilo sa použiť doplnkové skupiny zo súboru Compose", "Could not apply the Jellyfin configuration:": "Nepodarilo sa použiť konfiguráciu Jellyfin:", "Could not apply the installer profile": "Nepodarilo sa použiť profil inštalátora", "Could not apply the pre-start repair:": "Nepodarilo sa použiť predštartovaciu opravu:", @@ -1357,7 +1420,7 @@ "Could not download recovery blob from PBS.": "Nepodarilo sa stiahnuť obnovovací balíček z PBS.", "Could not download the NVIDIA Container Toolkit repository definition.": "Nepodarilo sa stiahnuť definíciu úložiska NVIDIA Container Toolkit.", "Could not download the NVIDIA Container Toolkit signing key.": "Nepodarilo sa stiahnuť podpisový kľúč NVIDIA Container Toolkit.", - "Could not download the image": "Nepodarilo sa stiahnuť obrázok", + "Could not download the image": "Obraz sa nepodarilo stiahnuť", "Could not download the installer.": "Inštalátor sa nepodarilo stiahnuť.", "Could not enable the privileged profile before the first start": "Nepodarilo sa povoliť privilegovaný profil pred prvým začiatkom", "Could not export ZFS pool": "ZFS pool sa nepodarilo exportovať", @@ -1375,8 +1438,8 @@ "Could not install sshpass automatically (no internet?). Falling back to manual paste mode — you'll see the line to copy onto the server next.": "sshpass sa nepodarilo nainštalovať automaticky (bez internetu?). Prepínam na ručné vloženie - hneď uvidíte riadok, ktorý treba skopírovať na server.", "Could not install systemd-zram-generator": "Nepodarilo sa nainštalovať systemd-zram-generator", "Could not install the NVIDIA Container Toolkit signing key.": "Nepodarilo sa nainštalovať podpisový kľúč NVIDIA Container Toolkit.", - "Could not install the required packages:": "Nepodarilo sa nainštalovať required balíčky:", - "Could not install the stack startup hook": "Nepodarilo sa nainštalovať stack štartovací hák", + "Could not install the required packages:": "Požadované balíky sa nepodarilo nainštalovať:", + "Could not install the stack startup hook": "Spúšťací skript zostavy sa nepodarilo nainštalovať", "Could not install vzdump hook in /etc/vzdump.conf": "Nepodarilo sa nainštalovať vzdump hook do /etc/vzdump.conf", "Could not load shared functions. Script cannot continue.": "Nepodarilo sa načítať zdieľané funkcie. Skript nemôže pokračovať.", "Could not load the host kernel module:": "Nepodarilo sa načítať modul hostiteľského jadra:", @@ -1392,7 +1455,7 @@ "Could not prepare the file bind mount target:": "Nepodarilo sa pripraviť cieľ pripojenia súboru:", "Could not publish pending restore. Previous pending restore was kept.": "Nepodarilo sa zverejniť čakajúce obnovenie. Predchádzajúce čakajúce obnovenie bolo zachované.", "Could not push the key. Check the password and that": "Kľúč sa nepodarilo odoslať. Skontrolujte heslo a to, že", - "Could not query the image registry": "Nepodarilo sa nájsť obrazový register", + "Could not query the image registry": "Nepodarilo sa získať údaje z registra obrazov", "Could not reach {registry}. Check its address, DNS and network access.": "Nepodarilo sa dosiahnuť {registry}. Skontrolujte jeho adresu, DNS a prístup k sieti.", "Could not read SMART data from": "SMART dáta sa nepodarilo prečítať z", "Could not read VM configuration.": "Nastavenie VM sa nepodarilo prečítať.", @@ -1500,10 +1563,10 @@ "Creating pigz wrapper script...": "Vytváram wrapper skript pre pigz...", "Creating the backup": "Vytvorenie zálohy", "Creating the container...": "Vytváram kontajner...", - "Creating the container: applying the image configuration...": "Vytváranie kontajnera: použitie konfigurácie obrázka...", - "Creating the container: extracting the image": "Vytvorenie kontajnera: extrahovanie obrázka", + "Creating the container: applying the image configuration...": "Vytváranie kontajnera: používajú sa nastavenia obrazu...", + "Creating the container: extracting the image": "Vytváranie kontajnera: rozbaľuje sa obraz", "Creating the container: preparing its disk...": "Vytváranie kontajnera: príprava jeho disku...", - "Creating the container: reading the image...": "Vytváranie kontajnera: čítanie obrázka...", + "Creating the container: reading the image...": "Vytváranie kontajnera: načítava sa obraz...", "Creating the initial administrator...": "Vytvorenie počiatočného správcu...", "Creating the private network...": "Vytváranie súkromnej siete...", "Creating the temporary data container": "Vytvorenie dočasného dátového kontajnera", @@ -1532,8 +1595,8 @@ "Current NFS client script supports privileged LXC only.": "Aktuálny skript NFS klienta podporuje iba privilegovaný LXC.", "Current NFS exports in CT": "Aktuálne NFS exporty v CT", "Current NFS mounts:": "Aktuálne NFS pripojenia:", - "Current NVIDIA inventory resolved: a refresh is required": "Aktuálne NVIDIA inventár vyriešený: obnovovanie je required", - "Current NVIDIA inventory resolved: no refresh is required": "Aktuálne NVIDIA inventár vyriešený: žiadne obnovenie je required", + "Current NVIDIA inventory resolved: a refresh is required": "Aktuálny zoznam zariadení NVIDIA bol zistený: je potrebné obnovenie", + "Current NVIDIA inventory resolved: no refresh is required": "Aktuálny zoznam zariadení NVIDIA bol zistený: obnovenie nie je potrebné", "Current Network Configuration": "Aktuálne nastavenie siete", "Current PBS Version": "Aktuálna verzia PBS", "Current PVE Version": "Aktuálna verzia PVE", @@ -1914,7 +1977,7 @@ "Downloading and verifying the image.\nThis can take a few minutes. Please wait...": "Obraz sa sťahuje a overuje.\nMôže to trvať niekoľko minút. Čakajte, prosím...", "Downloading official installer...": "Sťahujem oficiálny inštalátor...", "Downloading pre-existing encrypted backups from this host will fail unless you kept a copy of the current key.": "Stiahnutie už existujúcich šifrovaných záloh z tohto hosta zlyhá, ak ste si nenechali kópiu aktuálneho kľúča.", - "Downloading the image:": "Sťahovanie obrázku:", + "Downloading the image:": "Sťahuje sa obraz:", "Downloading the latest Fastfetch release...": "Sťahujem najnovšiu verziu Fastfetch...", "Driver blacklist entries removed": "Záznamy v blackliste ovládačov boli odstránené", "Driver blacklist removed for": "Blacklist ovládača odstránený pre", @@ -1922,7 +1985,7 @@ "Drivers :": "Ovládače :", "Drivers compiled and installed via DKMS.": "Ovládače boli skompilované a nainštalované cez DKMS.", "Dry run completed; mount configuration was not applied. The CT was already running.": "Suchý chod dokončený;konfigurácia pripojenia nebola použitá. CT už bežal.", - "Dry run completed; mount configuration was not applied. The CT was started for the remote check.": "Suchý chod dokončený;konfigurácia pripojenia nebola použitá. CT bol spustený na vzdialenú kontrolu.", + "Dry run completed; mount configuration was not applied. The CT was started for the remote check.": "Skúšobný beh bol dokončený; nastavenie pripojenia sa nepoužilo. Kontajner bol spustený na kontrolu vzdialeného úložiska.", "Dry run completed; no changes were made.": "Suchý beh dokončený; neboli vykonané žiadne zmeny.", "Dry run completed; no containers were created.": "Suchý beh dokončený; neboli vytvorené žiadne kontajnery.", "Dry run completed; the container and the mounts were not changed.": "Suchý chod dokončený; kontajner a držiaky neboli zmenené.", @@ -1938,7 +2001,7 @@ "Duplicated GPU device in the container": "Duplikované GPU zariadenie v kontajneri", "Duplicated NVIDIA devices": "Duplikované zariadenia NVIDIA", "Duplicated VMID in the Proxmox inventory": "Duplikované VMID v inventári Proxmox", - "Duplicated native directive:": "Duplikát natívne smernice:", + "Duplicated native directive:": "Duplicitná natívna direktíva:", "Duplicated native option": "Duplikované natívne možnosti", "Duplicated or invalid stack VMID": "Duplicitné alebo neplatné VMID kontajnerov aplikácie", "Duplicati is a backup client that securely stores encrypted, incremental, compressed backups on local storage, cloud storage services and remote file servers. It works with standard protocols like FTP, SSH, WebDAV as well as popular services like Microsoft OneDrive, Amazon S3, Google Drive, box.com, Mega, B2, and many others.": "Duplicati je záložný klient, ktorý bezpečne ukladá šifrované, prírastkové, stlačené zálohy na lokálne úložisko, cloudové úložné služby a vzdialené súborové servery. Funguje so štandardnými protokolmi ako FTP, SSH, WebDAV, rovnako ako populárne služby ako Microsoft OneDrive, Amazon S3, Google Drive, box.com, Mega, B2, a mnoho ďalších.", @@ -2738,8 +2801,8 @@ "Home Assistant": "Home Assistant", "Home Assistant Core - Open source home automation that puts local control and privacy first. Powered by a worldwide community of tinkerers and DIY enthusiasts. Perfect to run on a Raspberry Pi or a local server": "Home Assistant Core je systém domácej automatizácie s otvoreným zdrojovým kódom, ktorý uprednostňuje lokálne ovládanie a súkromie. Rozvíja ho celosvetová komunita nadšencov a domácich majstrov. Hodí sa na Raspberry Pi alebo lokálny server.", "Home Assistant OS cannot be checked without an IP address": "Home Assistant OS nemožno skontrolovať bez IP adresy", - "Home Assistant OS did not pass the Supervisor, Core and Observer checks": "Home Assistant OS neprešiel dozorcom, základnými a pozorovateľmi", - "Home Assistant OS ready: Supervisor, Core and Observer running": "Home Assistant OS pripravený: Supervízor, jadro a pozorovateľ beží", + "Home Assistant OS did not pass the Supervisor, Core and Observer checks": "Home Assistant OS neprešiel kontrolami Supervisor, Core a Observer", + "Home Assistant OS ready: Supervisor, Core and Observer running": "Home Assistant OS je pripravený: Supervisor, Core a Observer bežia", "Home Assistant OS was not started: its addresses will be known once Core is running.": "Home Assistant OS sa nezačalo: jeho adresy budú známe po spustení Core.", "Home Assistant Observer": "Home Assistant Observer", "Home Automation systems": "Systémy domácej automatizácie", @@ -2790,7 +2853,7 @@ "Host directories are not included in the backup and are not reverted by a recovery.": "Hostiteľské adresáre nie sú zahrnuté do zálohy a nie sú vrátené zotavením.", "Host directories are not included in the backups and are not reverted by a recovery.": "Hostiteľské adresáre nie sú zahrnuté v zálohách a nie sú vrátené zotavením.", "Host directories cannot be included in vzdump": "Hostiteľské adresáre nemožno zahrnúť do vzdump", - "Host directories cannot be part of the vzdump backup": "Hostiteľské adresáre nemôžu byť súčasťou zálohy", + "Host directories cannot be part of the vzdump backup": "Adresáre hostiteľa nemožno zahrnúť do zálohy vzdump", "Host directories that are kept, with their content:": "Hostiteľské adresáre, ktoré sú uchovávané, s ich obsahom:", "Host directory": "Adresár na serveri", "Host directory (created if it does not exist)": "Adresár na serveri (vytvorí sa, ak neexistuje)", @@ -2815,7 +2878,7 @@ "Host identity (hostname, hosts)": "Identita hosta (hostname, hosts)", "Host kernel module loaded:": "Načítaný modul jadra hostiteľa:", "Host monitor configured: shared PID and network namespaces, LXCFS disabled in this container": "Konfigurovaný monitor hostiteľa: zdieľané PID a sieťové priestory, LXCFS vypnuté v tomto kontajneri", - "Host monitor verified: PID and network namespaces and memory match the host": "Monitor hostiteľa overený: PID a sieťové názvy a pamäť zodpovedajú hostiteľovi", + "Host monitor verified: PID and network namespaces and memory match the host": "Monitor hostiteľa je overený: menné priestory PID a siete aj pamäť zodpovedajú hostiteľovi", "Host monitor: shared PID/network and privileged access. A compromised image could affect Proxmox. Use only on trusted networks.": "Monitorovanie hostiteľa so zdieľaným priestorom PID, sieťou a privilegovaným prístupom. Napadnutý obraz by mohol ohroziť Proxmox. Používajte iba v dôveryhodných sieťach.", "Host monitoring declined": "Monitorovanie hostiteľa bolo odmietnuté", "Host path for": "Cesta na hostiteľovi pre", @@ -2942,30 +3005,30 @@ "Image Source Directory": "Zdrojový priečinok obrazov", "Image access confirmed.\n\n{reference}\n\nArchitecture: {architecture}\nVersion: {version}\nDownload size: {size}\nDigest: {digest}": "Prístup k obrazu bol potvrdený.\n\n{reference}\n\nArchitektúra: {architecture}\nVerzia: {version}\nVeľkosť na stiahnutie: {size}\nDigest: {digest}", "Image cache": "Vyrovnávacia pamäť obrazov", - "Image compatibility restored:": "Kompatibilita s obrázkom obnovená:", - "Image compatibility verified:": "Kompatibilita s obrázkom overená:", + "Image compatibility restored:": "Kompatibilita obrazu bola obnovená:", + "Image compatibility verified:": "Kompatibilita obrazu bola overená:", "Image directory:": "Priečinok s obrazmi:", - "Image download failed:": "Sťahovanie obrázku zlyhalo:", - "Image downloaded": "Stiahnutý obrázok", + "Image download failed:": "Sťahovanie obrazu zlyhalo:", + "Image downloaded": "Obraz bol stiahnutý", "Image downloaded and verified.\n\n{reference}\n\nStorage: {storage}\nArchive size: {size}\nArchive: {archive}\n\nNo container was created.\nProxMenux removes unused cached images after successful updates.": "Obraz bol stiahnutý a overený.\n\n{reference}\n\nÚložisko: {storage}\nVeľkosť archívu: {size}\nArchív: {archive}\n\nNebol vytvorený žiadny kontajner.\nProxMenux po úspešných aktualizáciách odstraňuje nepoužívané obrazy z vyrovnávacej pamäte.", "Image file not found:": "Súbor obrazu sa nenašiel:", "Image imported:": "Obraz importovaný:", "Image integrity verification failed. The partial download was discarded; try again.": "Overenie integrity obrazu zlyhalo. Neúplne stiahnuté dáta boli zahodené; skúste to znova.", - "Image integrity verified": "Potvrdená integrita obrázku", - "Image not allowed for the host monitor profile": "Obrázok nie je povolený pre profil monitora hostiteľa", + "Image integrity verified": "Integrita obrazu bola overená", + "Image not allowed for the host monitor profile": "Obraz nie je povolený pre profil monitorovania hostiteľa", "Image reference": "Odkaz na obraz", "Image reference (for example ghcr.io/user/application:latest)": "Odkaz na obraz (napríklad ghcr.io/user/application:latest)", "Image selection was cancelled": "Výber obrazu bol zrušený", "Image that is not in the catalog": "Obraz, ktorý nie je v katalógu", - "Image:": "Image:", + "Image:": "Obraz:", "Images to import:": "Image na import:", - "Immich CUDA requires NVIDIA driver 545 or later": "Immich CUDA requires NVIDIA ovládač 545 alebo neskôr", + "Immich CUDA requires NVIDIA driver 545 or later": "Immich CUDA vyžaduje ovládač NVIDIA verzie 545 alebo novší", "Immich GPU profile not validated": "Profil GPU pre Immich nebol overený", "Immich configuration cancelled": "Konfigurácia Immich zrušená", "Immich device without a validated translation": "Zariadenie Immich bez overeného prekladu", "Immich machine learning": "Immich strojové učenie", - "Immich requires CUDA compute capability 5.2 or later": "Immich requires CUDA výpočtová schopnosť 5.2 alebo neskôr", - "Immich runtime without a validated translation": "Immich spustiť bez overeného prekladu", + "Immich requires CUDA compute capability 5.2 or later": "Immich vyžaduje výpočtovú schopnosť CUDA 5.2 alebo vyššiu", + "Immich runtime without a validated translation": "Prostredie Immich nemá overený prevod do natívneho LXC", "Immich server": "Server Immich", "Import — disk image imports": "Import — import diskových obrazov", "Import Disk Image to VM": "Importovať obraz disku do VM", @@ -3014,22 +3077,22 @@ "Incompatible instance directory": "Nekompatibilný inštančný adresár", "Incompatible instance record": "Nekompatibilný záznam o inštancii", "Incompatible record": "Nekompatibilný záznam", - "Incompatible stack assembly": "Nekompatibilná súprava stohov", + "Incompatible stack assembly": "Nekompatibilné zostavenie aplikácie", "Incompatible version": "Nekompatibilná verzia", "Incomplete NVIDIA identity": "Neúplná identita NVIDIA", "Incomplete NVIDIA inventory": "Neúplný inventár NVIDIA", "Incomplete Proxmox inventory": "Neúplný inventár Proxmox", "Incomplete Proxmox inventory; recovery blocked": "Neúplný inventár Proxmox; blokovaná obnova", - "Incomplete container removed:": "Neúplná odstránená nádoba:", + "Incomplete container removed:": "Nedokončený kontajner bol odstránený:", "Incomplete dependency order": "Neúplné poradie závislosti", "Incomplete device configuration": "Neúplná konfigurácia zariadenia", - "Incomplete file recipe or unknown paths": "Neúplný súbor recept alebo neznáme cesty", + "Incomplete file recipe or unknown paths": "Neúplný postup úpravy súborov alebo neznáme cesty", "Incomplete gzip layer": "Neúplná vrstva gzipu", "Incomplete mount configuration": "Neúplná konfigurácia pripojenia", "Incomplete or incompatible Proxmox inventory": "Neúplný alebo nezlučiteľný súpis Proxmox", "Incomplete primary network": "Neúplná primárna sieť", "Incomplete stack order": "Neúplné poradie kontajnerov aplikácie", - "Incomplete stack removed": "Neúplný zásobník odstránený", + "Incomplete stack removed": "Nedokončená zostava bola odstránená", "Inconsistent adaptation profile and recipe": "Nekonzistentný adaptačný profil a recept", "Inconsistent host monitor profile": "Profil nekonzistentného monitora hostiteľa", "Inconsistent stack identity": "Nekonzistentná identita aplikácie s viacerými kontajnermi", @@ -3043,7 +3106,7 @@ "Independent LXC applications installed": "Inštalované nezávislé aplikácie LXC", "Independent LXCs: no main container or hookscript. Each one keeps its own Start with Proxmox setting.": "Samostatné kontajnery LXC bez hlavného kontajnera a skriptu hookscript. Každý si zachová vlastné nastavenie spúšťania spolu s Proxmoxom.", "Independent applications, without a main container.": "Nezávislé aplikácie bez hlavného kontajnera.", - "Indexers and quality profiles still need to be configured.": "Indexery a kvalitné profily ešte treba nakonfigurovať.", + "Indexers and quality profiles still need to be configured.": "Indexery a profily kvality ešte treba nastaviť.", "Inherited retention:": "Prevzaté uchovávanie:", "Inherited schedule:": "Prevzatý plán:", "Initial Nextcloud administrator": "Počiatočný správca Nextcloud", @@ -3120,7 +3183,7 @@ "Installation completed. Press Enter to continue...": "Inštalácia je dokončená. Pokračujte stlačením Enter...", "Installation failed": "Inštalácia zlyhala", "Installation finished but drivers are not loaded. A reboot may be required.": "Inštalácia sa dokončila, ale ovládače nie sú načítané. Možno bude potrebný reštart.", - "Installation incomplete. These containers and their data are kept:": "Inštalácia neúplná. Tieto nádoby a ich údaje sa uchovávajú:", + "Installation incomplete. These containers and their data are kept:": "Inštalácia nie je dokončená. Tieto kontajnery a ich údaje zostávajú zachované:", "Installation log:": "Záznam inštalácie:", "Installation settings": "Nastavenia inštalácie", "Installation summary": "Zhrnutie inštalácie", @@ -3182,14 +3245,14 @@ "Installing pigz...": "Inštalujem pigz...", "Installing required dependencies...": "Inštalujem potrebné závislosti...", "Installing required package: git": "Inštalujem potrebný balík: git", - "Installing required packages...": "Inštalujem required balíčky...", + "Installing required packages...": "Inštalujú sa požadované balíky...", "Installing required tools...": "Inštalujem potrebné nástroje...", "Installing selected utilities": "Inštalujem vybrané nástroje", "Installing system utilities...": "Inštalujem systémové nástroje...", "Installing the new image": "Inštalácia nového obrazu", - "Installing the new image...": "Inštalujem nový obrázok...", - "Installing the new image:": "Inštalácia nového obrázku:", - "Installing the stack startup hook...": "Inštalácia stack startup...", + "Installing the new image...": "Inštaluje sa nový obraz...", + "Installing the new image:": "Inštaluje sa nový obraz:", + "Installing the stack startup hook...": "Inštaluje sa spúšťací skript zostavy...", "Installing zfs-auto-snapshot package...": "Inštalujem balík zfs-auto-snapshot...", "Installs essential packages if missing": "Nainštaluje chýbajúce základné balíky", "Insufficient Disk Space": "Nedostatok miesta na disku", @@ -3234,7 +3297,7 @@ "Interrupted stack operation found": "Našla sa prerušená operácia celej aplikácie", "Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.": "Neplatný kandidát 'proxmox-ve' (nie je 9.x alebo chýba). Skontrolujte nastavenie repozitárov a sieť, potom skúste znova.", "Invalid ALLOWED_HOSTS value": "Neplatná hodnota ALLOWED_HOSTS", - "Invalid Home Assistant OS check timeout": "Neplatný čas odhlásenia Home Assistant OS", + "Invalid Home Assistant OS check timeout": "Neplatný časový limit kontroly Home Assistant OS", "Invalid ID": "Neplatné ID", "Invalid IPv4 address": "Neplatná adresa IPv4", "Invalid IPv4 gateway": "Neplatná brána IPv4", @@ -3243,9 +3306,9 @@ "Invalid MAC address:": "Neplatná adresa MAC:", "Invalid NVIDIA destination": "Neplatné miesto určenia NVIDIA", "Invalid NVIDIA device:": "Neplatné NVIDIA zariadenie:", - "Invalid Nextcloud volume": "Neplatný objem Nextcloud", + "Invalid Nextcloud volume": "Neplatný dátový zväzok Nextcloud", "Invalid OCI Entrypoint": "Neplatný vstupný bod OCI", - "Invalid OCI digest": "Neplatná digescia OCI", + "Invalid OCI digest": "Neplatný identifikátor digest OCI", "Invalid OCR language:": "Neplatný jazyk OCR:", "Invalid Option": "Neplatná možnosť", "Invalid Path": "Neplatná cesta", @@ -3269,15 +3332,15 @@ "Invalid action": "Neplatná akcia", "Invalid administrator email": "Neplatný administrátorský mail", "Invalid administrator user name": "Neplatné meno správcu", - "Invalid base VMID": "Neplatná základňa VMID", + "Invalid base VMID": "Neplatné základné VMID", "Invalid bridge": "Neplatný sieťový most", "Invalid check package:": "Neplatný kontrolný balík:", "Invalid configuration path:": "Neplatná konfiguračná dráha:", - "Invalid consume/export volumes": "Neplatné objemy spotreby/vývozu", + "Invalid consume/export volumes": "Neplatné dátové zväzky consume a export", "Invalid container path:": "Neplatná trasa kontajnera:", - "Invalid credential file path:": "Neplatná cesta k credential súboru:", + "Invalid credential file path:": "Neplatná cesta súboru s prihlasovacími údajmi:", "Invalid declarative entrypoint": "Neplatný deklaratívny vstupný bod", - "Invalid declarative stop signal": "Neplatná deklaratívna zastávka signal", + "Invalid declarative stop signal": "Neplatný deklaratívny signál zastavenia", "Invalid declarative working directory": "Neplatný deklaratívny pracovný adresár", "Invalid device UID": "Neplatné UID zariadenia", "Invalid device UID:": "Neplatné UID zariadenie:", @@ -3290,12 +3353,12 @@ "Invalid health check": "Neplatná kontrola zdravotného stavu", "Invalid healthcheck path": "Neplatná cesta kontroly zdravotného stavu", "Invalid healthcheck port": "Neplatný port na kontrolu zdravotného stavu", - "Invalid healthcheck request timeout": "Neplatná lehota na kontrolu zdravotného stavu", + "Invalid healthcheck request timeout": "Neplatný časový limit požiadavky kontroly stavu", "Invalid healthcheck scheme": "Neplatná schéma kontroly zdravotného stavu", - "Invalid healthcheck stability period": "Neplatná doba stability zdravotného poistenia", + "Invalid healthcheck stability period": "Neplatné obdobie overovania stability", "Invalid healthcheck timeout": "Neplatná lehota na kontrolu zdravotného stavu", "Invalid host kernel module name:": "Neplatné meno modulu jadra hostiteľa:", - "Invalid host monitor PID": "Neplatný monitor hostiteľa PID", + "Invalid host monitor PID": "Neplatné PID monitora hostiteľa", "Invalid host path:": "Neplatná cesta k hostiteľovi:", "Invalid host-monitor firewall plan": "Neplatný plán firewallu pre monitor hostiteľa", "Invalid image probe descriptor": "Neplatný deskriptor obrazovej sondy", @@ -3307,14 +3370,14 @@ "Invalid machine learning resources": "Neplatné zdroje strojového učenia", "Invalid main member or duplicated members": "Neplatný hlavný člen alebo duplikovaný člen", "Invalid media path": "Neplatná mediálna cesta", - "Invalid media volume": "Neplatný objem média", - "Invalid mediafiles volume": "Neplatný objem súborov médií", + "Invalid media volume": "Neplatný dátový zväzok médií", + "Invalid mediafiles volume": "Neplatný dátový zväzok mediafiles", "Invalid minimum version:": "Neplatná minimálna verzia:", "Invalid mount name": "Neplatné meno pripojenia", "Invalid mount type": "Neplatný typ pripojenia", "Invalid name": "Neplatný názov", "Invalid name. Use only letters, numbers, hyphens and underscores.": "Neplatný názov. Použite iba písmená, čísla, pomlčky a podčiarkovníky.", - "Invalid native entrypoint": "Neplatný domáci vstupný bod", + "Invalid native entrypoint": "Neplatný natívny vstupný bod", "Invalid octal permissions": "Neplatné oktálne práva", "Invalid option": "Neplatná voľba", "Invalid option, please try again.": "Neplatná možnosť, skúste to znova.", @@ -3330,7 +3393,7 @@ "Invalid prlimit value": "Neplatná hodnota prlimitu", "Invalid process limits format": "Neplatný formát obmedzení procesu", "Invalid read-only option": "Neplatná možnosť iba na čítanie", - "Invalid registry digest": "Neplatné trávenie registra", + "Invalid registry digest": "Neplatný identifikátor digest z registra", "Invalid remote name": "Neplatný názov vzdialeného úložiska", "Invalid remote path": "Neplatná vzdialená cesta", "Invalid repair version:": "Neplatná verzia opravy:", @@ -3338,8 +3401,8 @@ "Invalid resources": "Neplatné zdroje", "Invalid restored volume path": "Neplatná cesta obnoveného objemu", "Invalid running state": "Neplatný stav spustenia", - "Invalid saved image digest": "Uložený image digest nie je platný", - "Invalid security.unprivileged value:": "Neplatné zabezpečenie.neprivilegovaná hodnota:", + "Invalid saved image digest": "Neplatný uložený identifikátor digest obrazu", + "Invalid security.unprivileged value:": "Neplatná hodnota security.unprivileged:", "Invalid selection": "Neplatný výber", "Invalid service alias": "Neplatný alias služby", "Invalid service check URL": "Neplatné URL služby", @@ -3348,17 +3411,17 @@ "Invalid shared path": "Neplatná spoločná cesta", "Invalid shutdown timeout": "Neplatný čas vypnutia", "Invalid size. Please enter a number in MB (e.g., 128, 256, 512).": "Neplatná veľkosť. Zadajte číslo v MB (napr. 128, 256, 512).", - "Invalid stack contract": "Neplatná zmluva o zásobníku", + "Invalid stack contract": "Neplatná definícia zostavy", "Invalid stack journal": "Neplatný záznam operácie aplikácie s viacerými kontajnermi", "Invalid stack members": "Neplatní členovia aplikácie s viacerými kontajnermi", - "Invalid stack name": "Neplatný názov stohu", + "Invalid stack name": "Neplatný názov zostavy", "Invalid stack operation": "Neplatná operácia aplikácie s viacerými kontajnermi", "Invalid storage": "Neplatné úložisko", "Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "Neplatné ID úložiska. Použite iba písmená, čísla, pomlčky a podčiarkovníky.", "Invalid storage name": "Neplatný názov úložiska", "Invalid suite application": "Neplatná aplikácia zostavy", "Invalid sysctl value:": "Neplatná sysctl hodnota:", - "Invalid template storage": "Neplatná pamäť šablóny", + "Invalid template storage": "Neplatné úložisko šablón", "Invalid template storage name.": "Neplatný názov úložiska šablóny.", "Invalid tmpfs options:": "Neplatné možnosti tmpfs:", "Invalid tmpfs path:": "Neplatná cesta tmpfs:", @@ -3401,8 +3464,8 @@ "Jackett works as a proxy server: it translates queries from apps (Sonarr, SickRage, CouchPotato, Mylar, etc) into tracker-site-specific http queries, parses the html response, then sends results back to the requesting software. This allows for getting recent uploads (like RSS) and performing searches. Jackett is a single repository of maintained indexer scraping & translation logic - removing the burden from other apps.": "Jackett funguje ako proxy server: dotazy z aplikácií Sonarr, SickRage, CouchPotato, Mylar a ďalších prevádza na HTTP dotazy pre jednotlivé trackery, spracuje ich HTML odpovede a výsledky vráti aplikácii. Umožňuje vyhľadávať aj získavať najnovšie príspevky cez RSS. Zdieľaná logika získavania a prevodu údajov z indexerov odbremeňuje ostatné aplikácie.", "Jellyfin WebUI": "Jellyfin WebUI", "Jellyfin configuration applied:": "Použitá konfigurácia Jellyfin:", - "Jellyfin did not create encoding.xml before the timeout": "Jellyfin nevytvoril kódovanie.xml pred prestávkou", - "Jellyfin has not created encoding.xml in any declared path": "Jellyfin nevytvoril kódovanie.xml v žiadnej deklarovanej ceste", + "Jellyfin did not create encoding.xml before the timeout": "Jellyfin nevytvoril encoding.xml pred uplynutím časového limitu", + "Jellyfin has not created encoding.xml in any declared path": "Jellyfin nevytvoril encoding.xml v žiadnej deklarovanej ceste", "Jellyseerr is a free and open source software application for managing requests for your media library.": "Jellyseerr je bezplatná a open source softvérová aplikácia pre správu žiadostí o mediálnu knižnicu.", "Jenkins Continuous Integration and Delivery server.": "Jenkins – server na priebežnú integráciu a dodávanie softvéru.", "Jenkins unlock": "Odomknutie Jenkins", @@ -3474,7 +3537,7 @@ "Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa číta nastavenia z /config/config.yml, kontajner však obsahuje iba /config/config.yml.template. Skopírujte šablónu do config.yml, vyplňte požadované pripojenia Plex a TMDb a kontajner reštartujte.", "Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga je mediálny server na komiksy, mangu, grafické romány, časopisy a elektronické knihy.", "Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita je profesionálny bezplatný maliarsky program s otvoreným zdrojovým kódom. Vytvárajú ho umelci, ktorí chcú sprístupniť umelecké nástroje každému.", - "LAN access to the kept containers (stack configuration incomplete):": "Prístup LAN k uloženým kontajnerom (konfigurácia koša neúplná):", + "LAN access to the kept containers (stack configuration incomplete):": "Prístup z LAN k ponechaným kontajnerom (nastavenie zostavy nie je dokončené):", "LAN address applied to the application URLs": "LAN adresa použitá na URL aplikácie", "LLM App Development Platform": "Platforma vývoja aplikácií LLM", "LM Studio can run local AI models like gpt-oss, Llama, Gemma, Qwen, and DeepSeek privately on your computer.": "LM Studio umožňuje spúšťať modely AI ako gpt-oss, Llama, Gemma, Qwen a DeepSeek lokálne a súkromne na vašom počítači.", @@ -3494,9 +3557,9 @@ "LXC containers with NVIDIA passthrough:": "LXC kontajnery s NVIDIA passthrough:", "LXC conversion from privileged to unprivileged completed successfully!": "Konverzia LXC z privilegovaného na neprivilegovaný kontajner bola úspešne dokončená.", "LXC conversion from unprivileged to privileged completed successfully!": "Konverzia LXC z neprivilegovaného na privilegovaný kontajner bola úspešne dokončená.", - "LXC entries outside the NVIDIA inventory of the journal": "LXC položiek mimo NVIDIA súpis časopisu", - "LXC entries outside the selected acceleration profile": "LXC položiek mimo zvoleného akceleračného profilu", - "LXC entry outside the read-only NVIDIA profile": "LXC vstup mimo profilu NVIDIA iba na čítanie", + "LXC entries outside the NVIDIA inventory of the journal": "Položky LXC mimo zoznamu zariadení NVIDIA v zázname operácie", + "LXC entries outside the selected acceleration profile": "Položky LXC mimo vybraného profilu zrýchlenia", + "LXC entry outside the read-only NVIDIA profile": "Položka LXC mimo profilu NVIDIA iba na čítanie", "LXC removed:": "LXC odstránený:", "LXC stopped": "LXC zastavený", "LXC update skipped by user.": "Aktualizácia LXC bola preskočená používateľom.", @@ -3643,7 +3706,7 @@ "Machine Type": "Typ stroja", "Machine learning": "Strojové učenie", "Machine learning container prepared": "Pripravený kontajner strojového učenia", - "Machine learning profile not implemented; it is not replaced by CPU:": "Strojový vzdelávací profil nie je implementovaný; nie je nahradený CPU:", + "Machine learning profile not implemented; it is not replaced by CPU:": "Profil strojového učenia nie je implementovaný; nenahradí sa CPU:", "Machine type: q35": "Typ stroja: q35", "Machine: q35": "Stroj: q35", "Main endpoint not yet defined": "Hlavný cieľ zatiaľ nedefinovaný", @@ -3725,10 +3788,10 @@ "Missing OCI metadata:": "Chýbajúce metaúdaje OCI:", "Missing commands after installation:": "Po inštalácii stále chýbajú tieto príkazy:", "Missing dependency": "Chýbajúca závislosť", - "Missing native directive:": "Chýbajúce domáce smernice:", + "Missing native directive:": "Chýbajúca natívna direktíva:", "Missing on target:": "Chýba na cieli:", "Missing or invalid parameter": "Parameter chýba alebo je neplatný", - "Missing required command:": "Chýba required príkaz:", + "Missing required command:": "Chýba požadovaný príkaz:", "Missing required parameter": "Chýba povinný parameter", "Mixed GPU Modes": "Rôzne režimy GPU", "Mixed current mode detected in selected GPU(s).": "Vybrané GPU sú v rôznych aktuálnych režimoch.", @@ -3790,12 +3853,12 @@ "Mount failed": "Pripojenie zlyhalo", "Mount mode applied": "Použitý režim montáže", "Mount name": "Názov pripojenia", - "Mount not authorized by the operation": "Pripojiť nie je povolené operation", + "Mount not authorized by the operation": "Toto pripojenie nie je operáciou povolené", "Mount options:": "Možnosti pripojenia:", "Mount or create it with its data and run the recovery again.": "Pripojte alebo vytvorte adresár s potrebnými údajmi a registráciu zopakujte.", "Mount path must be an absolute path starting with /": "Cesta pripojenia musí byť absolútna a začínať znakom /", "Mount path:": "Cesta pripojenia:", - "Mount paths must not overlap": "Pripojiť cesty sa nesmú prekrývať", + "Mount paths must not overlap": "Cesty pripojenia sa nesmú prekrývať", "Mount point created": "Mount point bol vytvorený", "Mount point created.": "Mount point bol vytvorený.", "Mount point is visible but NOT writable from inside the container": "Mount point je viditeľný, ale zvnútra kontajnera NIE JE zapisovateľný", @@ -3829,10 +3892,10 @@ "Move that copy offsite (USB, password manager, another host). Delete it from this path when done.": "Presuňte túto kópiu mimo servera (USB, správca hesiel, iný host). Po dokončení ju z tejto cesty odstráňte.", "Move to target VM (remove from source VM config)": "Presunúť do cieľovej VM (odstrániť zo zdrojovej VM)", "Moving the data volumes aside": "Presúvanie dátových zväzkov bokom", - "Moving the data volumes aside...": "Presunúť dáta nabok...", + "Moving the data volumes aside...": "Dátové zväzky sa presúvajú bokom...", "Multi-container application": "Aplikácia s viacerými kontajnermi", "Multi-container application (experimental)": "Použitie viacerých kontajnerov (experimentálne)", - "Multi-line variables are not supported": "Multi-line premenné nie sú podporované", + "Multi-line variables are not supported": "Viacriadkové premenné nie sú podporované", "Multiple networks or external networks are not yet supported": "Viaceré siete alebo externé siete zatiaľ nie sú podporované", "Multiple recovery groups found in PBS. Pick the one that originally created the keyfile:": "V PBS sa našlo viac obnovovacích skupín. Vyberte tú, ktorá pôvodne vytvorila keyfile:", "Multiple rootfs directories were found in this archive. Restore cannot continue automatically.": "V tomto archíve sa našlo viac priečinkov rootfs. Automatická obnova nemôže pokračovať.", @@ -3912,7 +3975,7 @@ "NVIDIA Actions": "Akcie pre NVIDIA", "NVIDIA CPU hiding already configured": "NVIDIA CPU skrytie už je nastavené", "NVIDIA Container Toolkit": "NVIDIA Container Toolkit", - "NVIDIA Container Toolkit could not generate the runtime inventory": "NVIDIA Container Toolkit nemohol vygenerovať časový inventár", + "NVIDIA Container Toolkit could not generate the runtime inventory": "NVIDIA Container Toolkit nedokázal vytvoriť zoznam súčastí prostredia NVIDIA", "NVIDIA Container Toolkit installed. GPU validation pending until the host restarts.": "Nainštalovaná súprava NVIDIA Container Toolkit. Čaká sa na overenie GPU, kým sa hostiteľ nereštartuje.", "NVIDIA Container Toolkit is incomplete. Missing:": "NVIDIA Container Toolkit je neúplná. Chýba:", "NVIDIA Container Toolkit is installed but its command line did not answer.": "NVIDIA Container Toolkit je nainštalovaný, ale jeho príkazový riadok neodpovedá.", @@ -3931,7 +3994,7 @@ "NVIDIA KVM hiding (cpu hidden=1)": "NVIDIA KVM skrytie (cpu hidden=1)", "NVIDIA KVM hiding already configured": "NVIDIA KVM skrytie už je nastavené", "NVIDIA Patch": "NVIDIA patch", - "NVIDIA device outside the expected native profile": "Zariadenie NVIDIA mimo očakávaného prirodzeného profilu", + "NVIDIA device outside the expected native profile": "Zariadenie NVIDIA mimo očakávaného natívneho profilu", "NVIDIA driver": "Ovládač NVIDIA", "NVIDIA driver installed successfully.": "Ovládač NVIDIA bol úspešne nainštalovaný.", "NVIDIA driver installed:": "Ovládač NVIDIA nainštalovaný:", @@ -3939,7 +4002,7 @@ "NVIDIA drivers are not installed or not loaded on this host.": "NVIDIA ovládače nie sú na tomto hostovi nainštalované alebo načítané.", "NVIDIA host services disabled for VFIO mode": "NVIDIA služby hosta vypnuté pre VFIO režim", "NVIDIA host services/autoload already aligned for native mode": "Služby/autoload NVIDIA na hoste už sú pripravené pre natívny režim", - "NVIDIA inside the container does not match the host driver or GPU": "NVIDIA vo vnútri kontajnera nezodpovedá hostiteľovi alebo GPU", + "NVIDIA inside the container does not match the host driver or GPU": "NVIDIA v kontajneri nezodpovedá ovládaču hostiteľa alebo GPU", "NVIDIA install incomplete. Check log:": "NVIDIA inštalácia nie je kompletná. Skontrolujte záznam:", "NVIDIA installer downloaded successfully": "NVIDIA inštalátor bol úspešne stiahnutý", "NVIDIA installer extracted.": "NVIDIA inštalátor rozbalený.", @@ -3965,7 +4028,7 @@ "NVIDIA uninstaller completed.": "NVIDIA odinštalovanie je dokončené.", "NVIDIA update failed for LXC": "Aktualizácia NVIDIA zlyhala pre LXC", "NVIDIA userspace libraries installed.": "NVIDIA userspace knižnice nainštalované.", - "NVML does not match the current host driver": "NVML nezodpovedá aktuálnemu hostiteľskému vodičovi", + "NVML does not match the current host driver": "NVML nezodpovedá aktuálnemu ovládaču hostiteľa", "NVMe Disk Detected": "Zistený NVMe disk", "NVMe critical_warning is 0 (no critical warnings reported).": "Kritické varovanie NVMe je 0 (žiadne kritické varovania nie sú hlásené).", "NVMe health status: PASSED": "Stav NVMe: V PORIADKU", @@ -4052,7 +4115,7 @@ "New backup job": "Nová úloha zálohy", "New backups on this host will be unencrypted until a new keyfile is set up.": "Nové zálohy na tomto hostovi budú nešifrované, kým nenastavíte nový keyfile.", "New image compatible:": "Nový obraz je kompatibilný:", - "New image installed": "Inštalovaný nový obrázok", + "New image installed": "Nový obraz je nainštalovaný", "New image installed, not verified yet": "Nový obraz je nainštalovaný, zatiaľ neoverený", "New image verified, not saved yet": "Nový obraz je overený, zatiaľ neuložený", "New kernel staged; rebuilding DKMS drivers:": "Nový kernel je pripravený; znovu zostavujem DKMS ovládače:", @@ -4246,7 +4309,7 @@ "No folders found in /mnt. Please create a new folder.": "V /mnt sa nenašli žiadne priečinky. Vytvorte nový priečinok.", "No folders found inside /mnt in the CT.": "V kontajneri sa v /mnt nenašli žiadne priečinky.", "No format-safe disks are available.": "Nie sú dostupné žiadne disky vhodné na bezpečné formátovanie.", - "No free ProxMenux private /24 network is available": "Nie je k dispozícii žiadna bezplatná sieť ProxMenux private /24", + "No free ProxMenux private /24 network is available": "Nie je dostupná žiadna voľná súkromná sieť ProxMenux /24", "No gasket DKMS registrations remain.": "Nezostávajú žiadne registrácie gasket DKMS.", "No group creation required — uses world-writable sticky bit permissions.": "Nie je potrebné vytvárať skupinu - používa sa zápis pre všetkých so sticky bitom.", "No guest selected.": "Nie je vybratý žiadny hosť.", @@ -4436,7 +4499,7 @@ "OVH server detection and RTM installation process completed": "Kontrola OVH servera a inštalácia RTM sú dokončené", "Object detector": "Detektor objektov", "Object detector for Frigate": "Detektor objektov pre Frigate", - "Observer is not responding on port 4357": "Pozorovateľ nereaguje na prístav 4357", + "Observer is not responding on port 4357": "Observer neodpovedá na porte 4357", "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian umožňuje vytvárať, prepájať a usporadúvať poznámky na vašom zariadení. Stovky doplnkov a tém prispôsobia pracovný postup. Poznámky môžete publikovať online, používať offline a bezpečne synchronizovať s koncovým šifrovaním.", "Offer as exit node?": "Ponúknuť ako exit node?", "Official Duplicati image: encrypted, incremental backups to local, network and cloud storage.": "Oficiálny obraz Duplicati: šifrované prírastkové zálohy do lokálneho, sieťového aj cloudového úložiska.", @@ -4496,8 +4559,8 @@ "OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw je osobný asistent AI, ktorý prevádzkujete na vlastných zariadeniach", "OpenList": "OpenList", "OpenShot Video Editor is an award-winning free and open-source video editor for Linux, Mac, and Windows, and is dedicated to delivering high quality video editing and animation solutions to the world.": "OpenShot Video Editor je ocenený bezplatný a open-source video editor pre Linux, Mac a Windows a je venovaný poskytovaniu kvalitných riešení pre editáciu videa a animácie svetu.", - "OpenVINO requires a CPU quota to keep the CPU topology": "OpenVINO requires a CPU kvóta pre udržanie topológie procesora", - "OpenVINO requires the render device of an Intel GPU": "OpenVINO requires zariadenie Intel GPU", + "OpenVINO requires a CPU quota to keep the CPU topology": "OpenVINO vyžaduje kvótu CPU na zachovanie topológie CPU", + "OpenVINO requires the render device of an Intel GPU": "OpenVINO vyžaduje vykresľovacie zariadenie GPU Intel", "OpenVSwitch installation could not be verified": "Inštaláciu OpenVSwitch sa nepodarilo overiť", "OpenVSwitch installed successfully": "OpenVSwitch bol úspešne nainštalovaný", "OpenVSwitch is ready to use": "OpenVSwitch je pripravený na použitie", @@ -4547,7 +4610,7 @@ "Original ZFS ARC config restored from .bak": "Pôvodné nastavenie ZFS ARC bolo obnovené zo súboru .bak", "Original bashrc restored": "Pôvodný bashrc bol obnovený", "Original logrotate configuration restored": "Pôvodné nastavenie logrotate bolo obnovené", - "Orphan stack contract archived:": "@ info: whatsthis", + "Orphan stack contract archived:": "Osirelá definícia zostavy bola archivovaná:", "Oscam is an Open Source Conditional Access Module software used for descrambling DVB transmissions using smart cards. It's both a server and a client.": "OSCam je modul podmieneného prístupu s otvoreným zdrojovým kódom na dekódovanie vysielania DVB pomocou čipových kariet. Funguje ako server aj klient.", "Other Prebuilt Linux VMs": "Ďalšie pripravené Linux VM", "Output archive:": "Výstupný archív:", @@ -4687,7 +4750,7 @@ "Peers reach the server through the public address and the UDP port given during the installation, so that port must be forwarded to this container.": "Účastníci sa pripájajú k serveru cez verejnú adresu a port UDP zadané počas inštalácie. Tento port musí byť presmerovaný do kontajnera.", "Peers to create: a number (3) or a list of names (phone,laptop)": "Účastníci na vytvorenie: počet (3) alebo zoznam mien (phone,laptop)", "Pelorus is an AI navigator for Selkies-powered Linux desktops. Pelorus runs a FastAPI server that gives an LLM agent (Ollama, OpenAI-compatible, or Gemini) control over mouse, keyboard, screenshot, and window management via the Pixelflux computer-use backend, a Linux accessibility tree (AT-SPI), and optional KWin D-Bus integration.": "Pelorus je navigátor AI pre linuxové pracovné plochy využívajúce Selkies. Server FastAPI dáva agentovi LLM (Ollama, model kompatibilný s OpenAI alebo Gemini) ovládanie myši, klávesnice, snímok obrazovky a správy okien cez backend Pixelflux, strom prístupnosti Linuxu AT-SPI a voliteľnú integráciu KWin D-Bus.", - "Pending components:": "Trvalé komponenty:", + "Pending components:": "Čakajúce komponenty:", "Pending restore ID:": "ID čakajúcej obnovy:", "Pending restore dir:": "Priečinok čakajúcej obnovy:", "Pending restore prepared. A reboot is required to complete it.": "Čakajúca obnova je pripravená. Na jej dokončenie je potrebný reštart.", @@ -4838,7 +4901,7 @@ "Privacy-first finance app with envelope budgeting and multi-device sync.": "Finančná aplikácia s dôrazom na súkromie, obálkovým rozpočtom a synchronizáciou viacerých zariadení.", "Privacy-first, self-hosted PDF toolkit": "Súprava nástrojov PDF na vlastnom serveri s dôrazom na súkromie", "Private installation record saved": "Uložené súkromné záznamy o inštalácii", - "Private network assigned automatically:": "Privátne siete pridelené automaticky:", + "Private network assigned automatically:": "Automaticky pridelená súkromná sieť:", "Private network kept, because other containers still use it:": "Súkromná sieť ponechaná, pretože ju stále používajú iné kontajnery:", "Private network kept, because other guests still use it:": "Súkromná sieť zachovaná, pretože ju stále používajú iné VM alebo CT:", "Private network of the application released:": "Súkromná sieť uvoľnenej žiadosti:", @@ -4995,10 +5058,10 @@ "RawTherapee is a free, cross-platform raw image processing program!": "RawTherapee je bezplatný multiplatformový program na spracovanie fotografií vo formáte RAW.", "Rclone WebUI": "Rclone WebUI", "Rclone mount": "Pripojenie Rclone", - "Rclone mount active": "Comment", + "Rclone mount active": "Pripojenie Rclone je aktívne", "Rclone mount needs a privileged LXC with FUSE access. The container is dedicated to Rclone and its web UI must not be exposed to untrusted networks.": "Pripojenie Rclone vyžaduje privilegovaný kontajner LXC s prístupom k FUSE. Kontajner je vyhradený pre Rclone a jeho webové rozhranie nesmie byť prístupné z nedôveryhodných sietí.", "Rclone mount requires a privileged LXC with FUSE access. Use this profile only on a trusted node and network.": "Pripojenie cez Rclone vyžaduje privilegovaný kontajner LXC s prístupom k FUSE. Tento profil používajte iba na dôveryhodnom uzle a v dôveryhodnej sieti.", - "Rclone mount requires a privileged container": "Rclon mount requires privilegovaný kontajner", + "Rclone mount requires a privileged container": "Pripojenie Rclone vyžaduje privilegovaný kontajner", "Re-enter the BORG REPOKEY passphrase to confirm:": "Zadajte BORG REPOKEY frázu znova na potvrdenie:", "Re-running pre-check after repairs...": "Spúšťam predbežnú kontrolu znova po opravách...", "Reachable": "Dostupné", @@ -5097,9 +5160,9 @@ "Recreated stack checked": "Znovu vytvorené kontajnery aplikácie sú skontrolované", "Recreated:": "Znovu vytvorené:", "Recreating": "Znovu vytváram", - "Recreating requires a confirmed proposal": "Obnovenie requires potvrdený návrh", - "Recreating the container...": "Obnovenie kontajnera...", - "Recreating the container:": "Obnovenie nádoby:", + "Recreating requires a confirmed proposal": "Opätovné vytvorenie vyžaduje potvrdený návrh", + "Recreating the container...": "Kontajner sa znova vytvára...", + "Recreating the container:": "Kontajner sa znova vytvára:", "Recreation completed. Data kept.": "Rekreácia dokončená. Údaje uchovávané.", "Recreation prepared": "Pripravené rekreácie", "Refresh APT index and verify repositories:": "Obnovte APT index a overte repozitáre:", @@ -5109,7 +5172,7 @@ "Refresh your browser to see changes (server restart may be required)": "Obnovte stránku v prehliadači, aby ste videli zmeny (môže byť potrebný reštart servera)", "Refreshing apt cache...": "Obnovujem apt cache...", "Refreshing the NVIDIA runtime...": "Obnovenie prevádzky NVIDIA...", - "Refusing an unexpected rootfs path:": "Odmietnutie nečakanej cesty ku koreňom:", + "Refusing an unexpected rootfs path:": "Odmietnutá neočakávaná cesta koreňového súborového systému:", "Regenerating PVE package cache...": "Znovu vytváram cache PVE balíkov...", "Regenerating boot artifacts for the merged kernel-agnostic changes...": "Znovu vytváram boot súbory pre zlúčené zmeny nezávislé od kernelu...", "Regenerating certificates and restarting services...": "Znovu generujem certifikáty a reštartujem služby...", @@ -5140,10 +5203,10 @@ "Reminder: You must install the QEMU Guest Agent inside the Windows VM": "Pripomienka: QEMU Guest Agent musíte nainštalovať aj priamo vo Windows VM", "Remmina is a remote desktop client written in GTK, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large or tiny screens. Remmina supports multiple network protocols, in an integrated and consistent user interface. Currently RDP, VNC, SPICE, SSH and EXEC are supported.": "Remmina je vzdialený desktopový klient napísaný v GTK, ktorého cieľom je byť užitočný pre správcov systémov a cestujúcich, ktorí musia pracovať s množstvom vzdialených počítačov pred veľkými alebo malými obrazovkami. Remmina podporuje viaceré sieťové protokoly v integrovanom a konzistentnom užívateľskom rozhraní. V súčasnosti sú podporované programy RDP, VNC, SPICE, SSH a EXEC.", "Remote Access & VPN": "Vzdialený prístup a VPN", - "Remote dry run completed; no container was created.": "Diaľkový suchý beh dokončený; nebola vytvorená žiadna nádoba.", + "Remote dry run completed; no container was created.": "Vzdialený skúšobný beh bol dokončený; žiadny kontajner sa nevytvoril.", "Remote repository path:": "Vzdialená cesta repozitára:", "Remote server via SSH (recommended — off-host, dedup across machines)": "Vzdialený server cez SSH (odporúčané - mimo hosta, deduplikácia medzi strojmi)", - "Remote verified:": "Diaľkové overenie:", + "Remote verified:": "Vzdialené úložisko je overené:", "Remounting CIFS share with open permissions...": "Znovu pripájam CIFS zdieľanie s otvorenými oprávneniami...", "Removal command finished; review any warnings above.": "Príkaz na odstránenie sa dokončil; skontrolujte všetky upozornenia vyššie.", "Remove CIFS Mount": "Odstrániť CIFS mount", @@ -5250,7 +5313,7 @@ "Removing storage from Proxmox...": "Odstraňujem úložisko z Proxmoxu...", "Removing system limits optimizations...": "Odstraňujem optimalizácie systémových limitov...", "Removing the containers...": "Odstránenie kontajnerov...", - "Removing the incomplete stack...": "Odstránenie neúplného zásobníka...", + "Removing the incomplete stack...": "Nedokončená zostava sa odstraňuje...", "Removing the previous container": "Odstránenie predchádzajúceho kontajnera", "Removing this path deletes its container volume and the data in it:": "Odstránením tejto cesty sa vymaže jej trvalé úložisko aj všetky údaje v ňom:", "Removing utilities installed by ProxMenux...": "Odstraňujú sa pomôcky nainštalované používateľom ProxMenux...", @@ -5286,11 +5349,11 @@ "Required if using a VirtIO or SCSI disk.": "Vyžaduje sa pri použití VirtIO alebo SCSI disku.", "Required install helpers not available.": "Potrební pomocníci na inštaláciu nie sú dostupní.", "Required new path cancelled": "Pridanie novej povinnej cesty bolo zrušené", - "Required persistent paths cannot be removed": "Required perzistentné cesty nemožno odstrániť", + "Required persistent paths cannot be removed": "Povinné cesty trvalého úložiska nemožno odstrániť", "Required update helpers unavailable. Update stopped.": "Požadovaní pomocníci s aktualizáciou nie sú k dispozícii. Aktualizácia zastavená.", "Requires acl package. Skip if setfacl is not available.": "Vyžaduje balík acl. Preskočte, ak setfacl nie je dostupný.", "Requires authentication": "Vyžaduje prihlásenie", - "Reserving a private network...": "Zachovanie súkromnej siete...", + "Reserving a private network...": "Rezervuje sa súkromná sieť...", "Reset Capability Blocked": "Reset zariadenia nie je použiteľný", "Reset Capability Warning": "Upozornenie na reset schopnosť", "Reset Monitor Password": "Obnoviť heslo Monitora", @@ -5379,7 +5442,7 @@ "Review what will be removed": "Skontrolujte, čo sa odstráni", "Rip DVD and Blu-ray media from a browser": "Kopírovanie médií DVD a Blu-ray cez prehliadač", "Rollback: nothing to remove (host matches backup)": "Rollback: nie je čo odstrániť (host zodpovedá zálohe)", - "Rolling back the incomplete container": "Zatáčanie nedokončenej nádoby", + "Rolling back the incomplete container": "Nedokončený kontajner sa odstraňuje v rámci návratu do pôvodného stavu", "RomM is a self-hosted ROM manager for managing and playing game collections.": "RomM je správca obrazov ROM na vlastnom serveri na správu a hranie herných zbierok.", "Root SSH keys/config": "SSH kľúče/nastavenie roota", "Root inside container = root on host system": "Root v kontajneri = root na hostiteľskom systéme", @@ -5402,7 +5465,7 @@ "Run a full security audit": "Spustiť úplnú bezpečnostnú kontrolu", "Run a job now": "Spustiť úlohu teraz", "Run apt-get install -f to complete any pending package configurations": "Spustite apt-get install -f na dokončenie všetkých čakajúcich konfigurácií balíkov", - "Run as root on the Proxmox node; the registry contains private data": "Spustiť ako koreň na uzle Proxmox; register obsahuje súkromné údaje", + "Run as root on the Proxmox node; the registry contains private data": "Spustite pod používateľom root na uzle Proxmox; register obsahuje súkromné údaje", "Run as server or client? [s/c]:": "Spustiť ako server alebo klient? [s/c]:", "Run checklist again to verify upgrade:": "Spustite kontrolný zoznam znova na overenie aktualizácie:", "Run from console, or SSH inside tmux/screen": "Spustiť z konzoly alebo cez SSH vo vnútri tmux/screen", @@ -5928,7 +5991,7 @@ "Skip — leave as-is": "Preskočiť - nechať bez zmeny", "Skipped (no disks of the pool are present on this host):": "Preskočené (na tomto hostovi nie sú prítomné žiadne disky poolu):", "Skipped (some disks missing):": "Preskočené (niektoré disky chýbajú):", - "Skipped because Jellyfin did not create encoding.xml:": "Preskočené, pretože Jellyfin nevytvoril kódovanie.xml:", + "Skipped because Jellyfin did not create encoding.xml:": "Preskočené, pretože Jellyfin nevytvoril encoding.xml:", "Skipped device": "Zariadenie bolo preskočené", "Skipped to protect target system (would cascade-remove packages)": "Preskočené na ochranu cieľového systému (odstránilo by to ďalšie balíky)", "Skipped, not in apt cache:": "Preskočené, nie je v apt cache:", @@ -5980,18 +6043,18 @@ "Stable (main branch)": "Stabilná verzia (vetva main)", "Stable monitor service normalized.": "Služba Monitora pre stabilnú verziu je upravená.", "Stack": "Stack", - "Stack adapter not recognized by the translator": "Stack adaptér nerozpoznal prekladateľ", + "Stack adapter not recognized by the translator": "Nástroj na prevod nerozpoznal adaptér zostavy", "Stack backups are missing; a partial restore is not allowed": "Chýbajú zálohy, čiastočná obnova nie je povolená.", "Stack checked:": "Aplikácia je skontrolovaná:", "Stack members are missing; recreate them after verifying their volumes": "Chýbajú kontajnery aplikácie; znova ich vytvorte až po overení ich dátových zväzkov", - "Stack members are updated together with their stack": "Členovia Stack sú aktualizované spolu s ich stack", + "Stack members are updated together with their stack": "Kontajnery zostavy sa aktualizujú spoločne s celou zostavou", "Stack name": "Názov zostavy", "Stack records restored": "Záznamy všetkých kontajnerov aplikácie sú obnovené", "Stack records saved": "Záznamy kontajnerov aplikácie sú uložené", - "Stack records saved; no container was reinstalled.": "Stack záznamy uložené; žiadny kontajner bol znovu nainštalovaný.", + "Stack records saved; no container was reinstalled.": "Záznamy zostavy boli uložené; žiadny kontajner nebol preinštalovaný.", "Stack recovery completed; every member is back to its previous installation.": "Obnova celej aplikácie je dokončená; všetky jej kontajnery sa vrátili k predchádzajúcej inštalácii.", "Stack recreation completed. Data kept.": "Opätovné vytvorenie kontajnerov aplikácie je dokončené. Údaje zostali zachované.", - "Stack startup hook installed": "Nainštalovaný štartovací hák Stack", + "Stack startup hook installed": "Spúšťací skript zostavy je nainštalovaný", "Stack stopped": "Kontajnery aplikácie sú zastavené", "Stack update completed. Data kept.": "Aktualizácia aplikácie je dokončená. Údaje zostali zachované.", "Stack:": "Stack:", @@ -6059,8 +6122,8 @@ "Starting the main container and its dependencies...": "Spustenie hlavného kontajnera a jeho závislosti...", "Starting the service:": "Spustenie služby:", "Starting unprivileged container...": "Spúšťam neprivilegovaný kontajner...", - "Startup: coordinated by the stack startup hook": "Štartovanie: koordinované stack štartovacím hákom", - "Startup: independent, without hookscript": "Spustenie: nezávislé, bez skriptu", + "Startup: coordinated by the stack startup hook": "Spúšťanie: koordinované spúšťacím skriptom zostavy", + "Startup: independent, without hookscript": "Spúšťanie: samostatné, bez hookscript", "Static IP": "Statické IP", "Static IPv4 address": "Statická IPv4 adresa", "Static IPv4 address for": "Statická IPv4 adresa pre", @@ -6078,7 +6141,7 @@ "Stop that VM, then choose:": "Zastavte VM a potom vyberte:", "Stop the CT, unmount the disk on the HOST, and remount with:": "Zastavte CT, odpojte disk na HOSTE a pripojte ho znova pomocou:", "Stop the VM/CT before formatting this disk.": "Pred formátovaním tohto disku zastavte VM/CT.", - "Stop the container before the NVIDIA refresh": "Zastavte nádobu pred obnovou NVIDIA", + "Stop the container before the NVIDIA refresh": "Pred obnovením NVIDIA zastavte kontajner", "Stop the container if it's running:": "Ak kontajner beží, zastavte ho:", "Stop them first and run this script again.": "Najprv ich zastavte a potom spustite skript znova.", "Stop uploading to PBS": "Zastaviť nahrávanie do PBS", @@ -6144,8 +6207,8 @@ "Subscription banner restored successfully (desktop and mobile)": "Hlásenie o predplatnom bolo úspešne obnovené (desktop aj mobil)", "Success": "Úspech", "Successful": "Úspešné", - "Supervisor does not confirm healthy and supported yet": "Dozorca zatiaľ nepotvrdil zdravie a podporu", - "Supervisor reports no connectivity; retrying to get versions and install components": "Supervízor nenahlási žiadnu konektivitu; znovu sa snaží získať verzie a inštalovať komponenty", + "Supervisor does not confirm healthy and supported yet": "Supervisor zatiaľ nepotvrdzuje zdravý a podporovaný stav", + "Supervisor reports no connectivity; retrying to get versions and install components": "Supervisor hlási neprítomnosť pripojenia; opakuje sa zisťovanie verzií a inštalácia komponentov", "Supported formats: .img, .qcow2, .vmdk, .raw": "Podporované formáty: .img, .qcow2, .vmdk, .raw", "Suspend - pauses the guest during backup": "Suspend – pozastaví hosťa počas zálohovania", "Swap": "Swap", @@ -6164,12 +6227,12 @@ "Switching to GPU -> VM mode requires exclusive VFIO binding.": "Prepnutie do režimu GPU -> VM vyžaduje výhradné VFIO naviazanie.", "Symbolic link in a restored volume path": "Symbolické spojenie v obnovenej ceste zväzku", "Symbolic link in the path of an adaptation": "Symbolický odkaz na ceste adaptácie", - "Symbolic link loop in the new image": "Symbolická slučka na novom obrázku", - "Symbolic link outside the rootfs of the new image": "Symbolický odkaz mimo rootfs nového obrázku", + "Symbolic link loop in the new image": "Slučka symbolických odkazov v novom obraze", + "Symbolic link outside the rootfs of the new image": "Symbolický odkaz smeruje mimo koreňového súborového systému nového obrazu", "Synchronize time automatically": "Automaticky synchronizovať čas", "Synclounge is a third party tool that allows you to watch Plex in sync with your friends/family, wherever you are.": "Synclounge je nástroj tretej strany, ktorý umožňuje sledovať Plex v synchronizácii so svojimi priateľmi / rodinou, kdekoľvek ste.", "Syncthing replaces proprietary sync and cloud services with something open, trustworthy and decentralized. Your data is your data alone and you deserve to choose where it is stored, if it is shared with some third party and how it's transmitted over the Internet.": "Syncthing nahrádza proprietárnu synchronizáciu a cloudové služby niečím otvoreným, dôveryhodným a decentralizovaným. Vaše údaje sú len vaše údaje a zaslúžite si vybrať, kde sú uložené, ak sú zdieľané s nejakou treťou stranou a ako sa prenášajú cez internet.", - "Sysctl not namespaced or not valid:": "Sysctl nie je menovaný alebo neplatný:", + "Sysctl not namespaced or not valid:": "Nastavenie sysctl nie je oddelené menným priestorom alebo je neplatné:", "System": "Systém", "System CLI Tools": "Systémové CLI nástroje", "System Disk Size (GB)": "Veľkosť systémového disku (GB)", @@ -6223,7 +6286,7 @@ "Telegram is a cloud-based mobile and desktop messaging app.": "Telegram je mobilná a desktopová aplikácia na báze cloudových správ.", "Temporary data container:": "Dočasný dátový kontajner:", "Temporary login": "Dočasné prihlásenie", - "Temporary password retrieved": "Dočasné obnovenie hesla", + "Temporary password retrieved": "Dočasné heslo bolo získané", "Temporary working directory (if present):": "Dočasný pracovný priečinok (ak existuje):", "Terminal Multiplexers": "Terminálové multiplexery", "Terminal multiplexer (Ctrl+b then d to detach, or type exit)": "Terminálový multiplexer (Ctrl+b potom d na odpojenie, alebo napíšte exit)", @@ -6247,9 +6310,9 @@ "Thank you for using ProxMenux. Goodbye!": "Ďakujeme, že používate ProxMenux. Dovidenia!", "That VM is currently stopped, so the GPU can be reassigned now.": "Tá VM je momentálne zastavená, takže GPU sa teraz dá presunúť.", "That doesn't look like an SSH private key. Pick the private key file (no .pub extension, parseable by ssh-keygen).": "Toto nevyzerá ako súkromný SSH kľúč. Vyberte súbor súkromného kľúča (bez prípony .pub, čitateľný cez ssh-keygen).", - "The AMD GPU did not complete a test inference with ROCm": "AMD GPU nedokončil testovanie s ROCm", + "The AMD GPU did not complete a test inference with ROCm": "GPU AMD nedokončilo skúšobný výpočet pomocou ROCm", "The AMD driver does not offer its compute interface (/dev/kfd) on this host.": "Ovládač AMD neponúka svoje výpočtové rozhranie (/dev/kfd) na tomto hostiteľovi.", - "The AppArmor/seccomp relaxation does not include the required consent": "AppArmor/seccomp relaxácia nezahŕňa súhlas required", + "The AppArmor/seccomp relaxation does not include the required consent": "Uvoľnenie obmedzení AppArmor/seccomp nemá požadovaný súhlas", "The Bookmark Everything App": "Aplikácia na ukladanie záložiek všetkého druhu", "The Brave browser is a fast, private and secure web browser for PC, Mac and mobile.": "Prehliadač Brave je rýchly, súkromný a bezpečný webový prehliadač pre PC, Mac a mobil.", "The CT already exists:": "CT už existuje:", @@ -6259,15 +6322,15 @@ "The Compose file does not contain a Compose document": "Súbor neobsahuje dokument Compose", "The Compose file is not valid YAML:": "Súbor Compose nie je platný YAML:", "The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. Continue only if you trust the image and accept this risk.": "Súbor Compose ponúka voliteľné uvoľnenie obmedzení AppArmor alebo seccomp, ktoré zostane vypnuté, kým ho používateľ nevyberie. Pokračujte iba vtedy, ak obrazu dôverujete a riziko prijímate.", - "The Compose value must be text or a list:": "Kompozitná hodnota musí byť text alebo zoznam:", - "The DRM node is not an Intel or AMD GPU; NVIDIA requires its library profile": "DRM uzol nie je Intel ani AMD GPU; NVIDIA requires svoj profil knižnice", - "The Entrypoint/Cmd combination is empty": "Vstupný bod/Cmd combination je prázdny", + "The Compose value must be text or a list:": "Hodnota Compose musí byť text alebo zoznam:", + "The DRM node is not an Intel or AMD GPU; NVIDIA requires its library profile": "Uzol DRM nie je GPU Intel ani AMD; NVIDIA vyžaduje profil so svojimi knižnicami", + "The Entrypoint/Cmd combination is empty": "Kombinácia Entrypoint/Cmd je prázdna", "The FUSE publication helper was not found": "Pomocník FUSE nebol nájdený", "The GPU evidence does not match the verified devices": "Dôkazy GPU nezodpovedajú overeným zariadeniam", "The GPU has been moved out of VM": "GPU bola presunutá mimo VM", "The GPU identity or permissions changed; the container is not modified": "Identita alebo povolenia GPU sa zmenili; kontajner sa nemení", "The GPU is being detached from VM": "GPU sa odpája z VM", - "The GPU vendor differs from the requested profile": "Predajca GPU sa líši od požadovaného profilu", + "The GPU vendor differs from the requested profile": "Výrobca GPU nezodpovedá požadovanému profilu", "The GPU vendor does not match the selected GPU profile:": "Dodávateľ GPU nezodpovedá zvolenému profilu GPU:", "The Immich CPU quota cannot be reproduced": "Kvóta Immich CPU nemôže byť reprodukovaná", "The Immich library needs at least 8 GB": "Knižnica Immich potrebuje aspoň 8 GB", @@ -6276,7 +6339,7 @@ "The LXC changed while reviewing; nothing was adopted": "LXC sa zmenil počas kontroly;nič nebolo prijaté", "The LXC could not be started.": "LXC sa nepodarilo spustiť.", "The LXC has no Bash to run this script with. The addon was not run.": "LXC nemá Bash na spustenie tohto skriptu. Doplnok nebol spustený.", - "The LXC has stopped": "LXC zastavil", + "The LXC has stopped": "Kontajner LXC sa zastavil", "The LXC is stopped. Do you want to start it to run the addon?": "LXC je zastavený. Chcete ho spustiť, aby sa spustil doplnok?", "The Lounge starts in public mode: anyone who reaches the address opens the client without logging in, and the IRC networks added are lost when the session ends.": "The Lounge začína vo verejnom režime: každý s prístupom na adresu otvorí klienta bez prihlásenia. Pridané siete IRC sa po skončení relácie stratia.", "The MAC address of the container cannot be kept": "Adresa MAC kontajnera sa nemôže uchovávať", @@ -6284,10 +6347,10 @@ "The NVIDIA Container Toolkit signing key could not be read.": "Nepodarilo sa prečítať podpisový kľúč súpravy NVIDIA Container Toolkit.", "The NVIDIA Container Toolkit version cannot be identified": "Verzia NVIDIA Container Toolkit sa nedá identifikovať", "The NVIDIA destination cannot be replaced": "Miesto určenia NVIDIA nemožno nahradiť", - "The NVIDIA destination escapes the rootfs": "Cieľ NVIDIA unikne koreňom", + "The NVIDIA destination escapes the rootfs": "Cieľ NVIDIA smeruje mimo koreňového súborového systému", "The NVIDIA driver is installed, but the Container Toolkit phase did not complete. GPU support for OCI containers is unavailable until it does.": "Ovládač NVIDIA je nainštalovaný, ale fáza Container Toolkit sa nedokončila. Podpora GPU pre kontajnery OCI nie je k dispozícii, kým sa nestane.", - "The NVIDIA driver or inventory changed; the operation was stopped": "Ovládač NVIDIA alebo inventár sa zmenili; operation bol zastavený", - "The NVIDIA hook or environment differs from the declared one": "Háčik alebo prostredie NVIDIA sa líši od deklarovaného", + "The NVIDIA driver or inventory changed; the operation was stopped": "Ovládač alebo zoznam zariadení NVIDIA sa zmenil; operácia bola zastavená", + "The NVIDIA hook or environment differs from the declared one": "Spúšťací skript alebo prostredie NVIDIA sa líši od deklarovaného", "The NVIDIA hook path does not belong to the installer": "Cesta k háku NVIDIA nepatrí inštalátorovi", "The NVIDIA installer needs at least": "NVIDIA inštalátor potrebuje aspoň", "The NVIDIA runtime is up to date; the container is not modified or started": "Runtime NVIDIA je aktuálny; kontajner nie je upravený ani spustený", @@ -6297,13 +6360,13 @@ "The OCI archive does not exist or is empty:": "Archív OCI neexistuje alebo je prázdny:", "The OCI archive verifier was not found": "Archívový overovateľ OCI nebol nájdený", "The OCI catalog is not installed. Update ProxMenux and try again.": "Katalóg OCI nie je nainštalovaný. Aktualizujte ProxMenux a skúste to znova.", - "The OCI contract changed while reviewing; nothing was adopted": "Zmluva OCI sa zmenila počas kontroly;nič nebolo prijaté", - "The OCI engine is not installed. Update ProxMenux and try again.": "Motor OCI nie je nainštalovaný. Aktualizovať ProxMenux a skúste to znova.", + "The OCI contract changed while reviewing; nothing was adopted": "Definícia OCI sa počas kontroly zmenila; nič sa neprevzalo", + "The OCI engine is not installed. Update ProxMenux and try again.": "Engine OCI nie je nainštalovaný. Aktualizujte ProxMenux a skúste to znova.", "The OCI image storage was not kept": "Uskladnenie obrazu OCI nebolo uchované", "The OCR language must use Tesseract codes, for example eng or eng+spa": "Jazyk OCR musí používať kódy Tesseract, napríklad eng alebo eng+spa", - "The PATH of the new image is outside the reproducible profile": "PATH nového obrázku je mimo reprodukovateľného profilu", + "The PATH of the new image is outside the reproducible profile": "Premenná PATH nového obrazu je mimo profilu, ktorý možno zopakovať", "The Paperless persistent volumes need at least 8 GB": "Trvalé dátové zväzky aplikácie Paperless musia mať najmenej 8 GB", - "The PostgreSQL volume needs at least 4 GB": "Objem PostgreSQL potrebuje najmenej 4 GB", + "The PostgreSQL volume needs at least 4 GB": "Dátový zväzok PostgreSQL musí mať najmenej 4 GB", "The PostgreSQL volume needs at least 8 GB": "Dátový zväzok PostgreSQL musí mať najmenej 8 GB", "The ProxMenux Monitor update checker is not available. Start or update ProxMenux Monitor and try again.": "Kontrola aktualizácií v ProxMenux Monitor nie je dostupná. Spustite alebo aktualizujte ProxMenux Monitor a skúste to znova.", "The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "Aktualizácia Proxmox VE 8 sa nedokončila. Prechod na Proxmox VE 9 sa nespustil.", @@ -6324,11 +6387,11 @@ "The VM is powered on. Turn it off before adding disks.": "VM je zapnutá. Pred pridaním diskov ju vypnite.", "The VM/LXC will lose access to this disk after formatting.": "VM/LXC po formátovaní stratí prístup k tomuto disku.", "The VMID belongs to another container now and is not touched:": "VMID teraz patrí inému kontajneru; ten zostane nedotknutý:", - "The VMID or its contract is already in use; it is not adopted": "VMID alebo jeho zmluva sa už používa; nie je prijatá", + "The VMID or its contract is already in use; it is not adopted": "VMID alebo jeho definícia sa už používa; neprevezme sa", "The VMID was reused or its identity is unknown; the operation is blocked": "VMID bolo znova použité alebo identita kontajnera nie je známa; operácia je zablokovaná", "The VMID was reused or the container is on another node; it is not overwritten": "VMID bol opätovne použitý alebo kontajner je na inom uzle; nie je prepísaný", - "The VMID was taken during the installation:": "VMID bol vykonaný počas inštalácie:", - "The Valkey volume needs at least 1 GB": "Objem Valkey potrebuje aspoň 1 GB", + "The VMID was taken during the installation:": "VMID počas inštalácie obsadil iný proces:", + "The Valkey volume needs at least 1 GB": "Dátový zväzok Valkey musí mať najmenej 1 GB", "The acceleration evidence differs from the verified inventory": "Dôkazy zrýchlenia sa líšia od overeného súpisu", "The acceleration profile does not support this architecture:": "Profil zrýchlenia nepodporuje túto architektúru:", "The access rule added to this host directory is kept:": "Pravidlo prístupu pridané do tohto hostiteľského adresára sa zachová:", @@ -6336,7 +6399,7 @@ "The active kernel driver is not vfio-pci, but the entry will rebind the GPU to vfio-pci on the next reboot, breaking the LXC passthrough about to be configured.": "Aktívny ovládač jadra nie je vfio-pci, ale táto položka pri ďalšom reštarte znova naviaže GPU na vfio-pci, čím sa preruší prechod LXC, ktorý sa má konfigurovať.", "The active kernel driver is not vfio-pci, but the entry will rebind the GPU to vfio-pci on the next reboot.": "Aktívny ovládač jadra nie je vfio-pci, ale táto položka pri ďalšom reštarte znova pripojí GPU k vfio-pci.", "The adaptation content was modified": "Obsah úpravy bol upravený", - "The added disk needs a whole-GiB size recorded by Proxmox": "Pridaný disk potrebuje veľkosť celého GiB zaznamenanú Proxmox", + "The added disk needs a whole-GiB size recorded by Proxmox": "Pridaný disk musí mať v Proxmoxe zaznamenanú veľkosť v celých GiB", "The added disk size is not a whole GiB": "Pridaná veľkosť disku nie je celý GiB", "The additional path hides a system directory": "Ďalšia cesta skrýva systémový adresár", "The address is already assigned on this host or cluster:": "Adresa je už priradená na tomto hostiteľovi alebo v klastri:", @@ -6349,10 +6412,10 @@ "The application could not be modified:": "Aplikáciu nebolo možné upraviť:", "The application could not be recovered:": "Aplikáciu sa nepodarilo znova zaregistrovať:", "The application could not be recreated:": "Aplikáciu nebolo možné znova vytvoriť:", - "The application did not complete its initial setup:": "Žiadosť nevyplnila svoje pôvodné nastavenie:", + "The application did not complete its initial setup:": "Aplikácia nedokončila úvodné nastavenie:", "The application did not get an address on the access network:": "Aplikácia nedostala adresu v prístupovej sieti:", "The application did not pass its HTTP check:": "Aplikácia neprešla kontrolou HTTP:", - "The application did not respond in time:": "Žiadosť neodpovedala včas:", + "The application did not respond in time:": "Aplikácia neodpovedala včas:", "The application has been modified with the new options.": "Nastavenia aplikácie boli upravené.", "The application has been recreated with the new options.": "Aplikácia bola znovu vytvorená s novými možnosťami.", "The application needs it for": "Aplikácia to potrebuje pre", @@ -6361,7 +6424,7 @@ "The application was removed": "Aplikácia bola odstránená", "The archive could not be extracted.": "Archív sa nepodarilo rozbaliť.", "The archive destination directory is INSIDE one of the paths you are about to back up. Writing the archive there would copy the backup into itself — producing a corrupted archive, or growing without limit until the disk fills up.": "Cieľový priečinok archívu je VNÚTRI jednej z ciest, ktoré sa chystáte zálohovať. Zápis archívu na toto miesto by zálohu kopíroval samu do seba - vznikol by poškodený archív alebo by rástol, kým sa disk nezaplní.", - "The backup could not be identified; the image is not replaced": "Zálohovanie nebolo možné identifikovať; obrázok nie je nahradený", + "The backup could not be identified; the image is not replaced": "Zálohu sa nepodarilo identifikovať; obraz sa nenahradí", "The backup could not be started on node": "Zálohovanie nebolo možné spustiť na uzle", "The backup did not pass its integrity check; creating it again...": "Záloha neprešla kontrolou integrity;vytváram to znova...", "The backup made before changing the application does not fit on the system disk of the host.": "Záloha vytvorená pred zmenou aplikácie sa nezmestí na systémový disk hostiteľa.", @@ -6372,11 +6435,11 @@ "The backup storage does not exist:": "Úložisko záloh neexistuje:", "The backup was altered; recovery blocked": "Záloha bola zmenená; obnova zablokovaná", "The backup was taken on a different PVE or kernel major.minor. These paths will be SKIPPED to keep the boot safe:": "Záloha bola vytvorená na inom PVE alebo inom major.minor kerneli. Tieto cesty sa PRESKOČIA, aby zostal štart bezpečný:", - "The bind mount target escapes the rootfs:": "Záväzná pripojiť cieľ unikne koreňom:", - "The block device does not exist:": "Blokovacie zariadenie neexistuje:", + "The bind mount target escapes the rootfs:": "Cieľ pripojenia bind smeruje mimo koreňového súborového systému:", + "The block device does not exist:": "Blokové zariadenie neexistuje:", "The build could not be checked beforehand; continuing without that check.": "Zostavenie nebolo možné vopred skontrolovať;pokračovanie bez tejto kontroly.", "The cached image does not match the current digest": "Comment", - "The cached image is damaged; it will be downloaded again.": "Comment", + "The cached image is damaged; it will be downloaded again.": "Obraz vo vyrovnávacej pamäti je poškodený; stiahne sa znova.", "The changes need the application installer": "Zmeny vyžadujú inštalačný program aplikácie", "The character device does not exist:": "Znakové zariadenie neexistuje:", "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Príkaz sa opýta na heslo, ktoré sa pri písaní nezobrazuje. Po vytvorení používateľov bude webové rozhranie vyžadovať používateľské meno a heslo.", @@ -6390,21 +6453,21 @@ "The configuration changed after the new container was validated": "Konfigurácia zmenená po overení nového kontajnera", "The configuration changed during the NVIDIA refresh": "Konfigurácia sa zmenila počas obnovovania NVIDIA", "The configuration evidence does not match": "Konfiguračný dôkaz nezodpovedá", - "The configuration must run as root on Proxmox VE": "Konfigurácia musí bežať ako koreň na Proxmox VE", + "The configuration must run as root on Proxmox VE": "Nastavenie musí bežať pod používateľom root na Proxmox VE", "The configuration of a new member changed after it was created": "Nastavenie nového kontajnera sa po jeho vytvorení zmenilo", "The configuration stopped because of an unexpected error": "Konfigurácia sa zastavila kvôli neočakávanej chybe", "The configured credential helper could not be used. Check its installation and credential store.": "Nakonfigurovaný pomocný program na správu prihlasovacích údajov sa nedá použiť. Skontrolujte jeho inštaláciu a úložisko prihlasovacích údajov.", "The console of the container could not be configured": "Konzolu kontajnera nebolo možné nakonfigurovať", "The consume/export volumes need at least 1 GB": "Dátové zväzky pre consume a export musia mať najmenej 1 GB", - "The container could not be removed automatically:": "Nádoba sa nedala automaticky odstrániť:", - "The container could not be started:": "Nádoba nemohla byť spustená:", + "The container could not be removed automatically:": "Kontajner sa nepodarilo automaticky odstrániť:", + "The container could not be started:": "Kontajner sa nepodarilo spustiť:", "The container creation does not match the prepared instance": "Vytvorenie kontajnera nezodpovedá pripravenej inštancii", "The container devices do not match the saved record": "Zariadenie kontajnera nezodpovedá uloženému záznamu", - "The container did not stop to update its persistent configuration:": "Kontajner neprestal aktualizovať svoju pretrvávajúcu konfiguráciu:", + "The container did not stop to update its persistent configuration:": "Kontajner sa nezastavil na aktualizáciu svojich trvalých nastavení:", "The container did not stop; its disks are not touched": "Kontajner sa nezastavil; jeho disky nie sú dotknuté", "The container disks do not match the saved record": "disky kontajnera nezodpovedajú uloženému záznamu", - "The container does not exist:": "Nádoba neexistuje:", - "The container does not have the fuse=1 feature enabled": "Kontajner nemá poistky = 1 zapnuté", + "The container does not exist:": "Kontajner neexistuje:", + "The container does not have the fuse=1 feature enabled": "Kontajner nemá zapnutú funkciu fuse=1", "The container does not need it any more; an update downloads the new version when there is one.": "Kontajner ho už nepotrebuje. Pri aktualizácii sa stiahne nový obraz, ak je dostupný.", "The container gets its own address and its own volumes, so the networks and volumes declared in the file are not used.": "Kontajner dostane vlastnú adresu a vlastné dátové úložiská, preto sa siete a úložiská definované v súbore nepoužijú.", "The container has advanced Proxmox settings outside the supported profile": "Kontajner má pokročilé nastavenia Proxmox mimo podporovaného profilu", @@ -6417,17 +6480,17 @@ "The container is not modified because a host directory is not available:": "Kontajner nie je modifikovaný, pretože adresár hostiteľa nie je k dispozícii:", "The container is restarted to apply the changes. Its own data, the database and the other containers of the application are not touched.": "Kontajner sa reštartuje, aby sa zmeny uplatnili. Jeho vlastné údaje, databáza a ostatné kontajnery aplikácie nie sú dotknuté.", "The container no longer exists:": "Kontajner už neexistuje:", - "The container of a member was replaced; the assembly is not resumed": "Zásobník člena bol nahradený; zhromaždenie sa neobnovuje", + "The container of a member was replaced; the assembly is not resumed": "Kontajner zostavy bol nahradený; zostavovanie nebude pokračovať", "The container runs on another node of the cluster; migrate it back to this node to remove it:": "Kontajner beží na inom uzle klastra; migrujte ho späť do tohto uzla, aby ste ho odstránili:", "The container should now start as privileged": "Kontajner by sa teraz mal spustiť ako privilegovaný", "The container should now start as unprivileged": "Kontajner by sa teraz mal spustiť ako neprivilegovaný", - "The container stopped after starting:": "Nádoba sa zastavila po začatí:", - "The container stopped before publishing the mount": "Kontajner sa zastavil pred zverejnením vrchu", + "The container stopped after starting:": "Kontajner sa po spustení zastavil:", + "The container stopped before publishing the mount": "Kontajner sa zastavil pred sprístupnením pripojenia", "The container stopped before the GPU permissions were verified:": "Kontajner sa zastavil pred overením povolení GPU:", - "The container stopped before the application responded:": "Nádobka sa pred podaním žiadosti zastavila:", + "The container stopped before the application responded:": "Kontajner sa zastavil pred odpoveďou aplikácie:", "The container stopped:": "Kontajner sa zastavil:", "The container takes its time zone from Proxmox, so the time files of the host are not attached to it.": "Kontajner preberá časové pásmo z Proxmoxu, preto sa doň nepripájajú súbory hostiteľa s nastavením času.", - "The container was changed outside ProxMenux and an update would discard those changes:": "Kontajner bol zmenený mimo ProxMenux a aktualizácia by sa zbaviť týchto zmien:", + "The container was changed outside ProxMenux and an update would discard those changes:": "Kontajner bol zmenený mimo ProxMenuxu a aktualizácia by tieto zmeny zahodila:", "The container was created from a downloaded OCI image": "Kontajner bol vytvorený zo stiahnutého obrazu OCI", "The containers do not need them any more; an update downloads the new versions when there are any.": "Kontajnery ich už nepotrebujú; aktualizácia stiahne nové verzie, ak nejaké existujú.", "The containers were created from downloaded OCI images": "Kontajnery boli vytvorené zo stiahnutých obrazov OCI", @@ -6441,17 +6504,17 @@ "The current template requires a new persistent path:": "Aktuálna šablóna vyžaduje nové trvalé úložisko pre cestu:", "The custom path cannot hide system directories": "Vlastná cesta nemôže skryť systémové adresáre", "The custom path overlaps another mount": "Vlastná cesta sa prekrýva s iným bodom pripojenia", - "The data and document volumes need at least 8 GB": "Údaje a objemy dokumentov potrebujú najmenej 8 GB", + "The data and document volumes need at least 8 GB": "Dátové zväzky pre údaje a dokumenty musia mať najmenej 8 GB", "The database server must accept connections from the IP address of this container, with a user that is not limited to localhost.": "Databázový server musí prijímať pripojenia z IP adresy tohto kontajnera s užívateľom, ktorý nie je obmedzený na localhost.", - "The dedicated adapter still requires replaying its rootfs changes": "Špecializovaný adaptér stále requires prehráva svoje rootfs zmeny", + "The dedicated adapter still requires replaying its rootfs changes": "Vyhradený adaptér stále vyžaduje zopakovanie zmien koreňového súborového systému", "The dependency hook and the stack recipe differ": "Spúšťací skript závislostí nezodpovedá konfigurácii aplikácie", "The dependency hook was modified; review it before updating": "Spúšťací skript závislostí bol zmenený; pred aktualizáciou ho skontrolujte", "The developer-friendly cloud platform for building and running LLM agents for AI-native applications.": "Cloudová platforma pre vývojárov na tvorbu a prevádzku agentov LLM v aplikáciách postavených na AI.", "The device directory does not exist:": "Adresár zariadenia neexistuje:", - "The device must keep its /dev path inside the LXC:": "Zariadenie musí udržať svoju / Dev cestu vnútri LXC:", + "The device must keep its /dev path inside the LXC:": "Zariadenie musí mať v LXC zachovanú svoju cestu /dev:", "The device must keep its native path without duplicates": "Zariadenie musí udržiavať svoju pôvodnú cestu bez duplikátov", "The device to remove is not attached:": "Zariadenie na odstránenie nie je pripojené:", - "The directory contains no character devices:": "Adresár neobsahuje žiadne znaky zariadenia:", + "The directory contains no character devices:": "Adresár neobsahuje žiadne znakové zariadenia:", "The directory does not exist in the CT.": "Priečinok v CT neexistuje.", "The disk": "Disk", "The disk does not belong to this CT; automatic adoption is unsafe": "Disk nepatrí k tomuto CT;automatická adopcia nie je bezpečná", @@ -6459,23 +6522,23 @@ "The disk usage of the container could not be read": "Použitie disku kontajnera nebolo možné prečítať", "The disks of the failed attempt could not be removed and stay in the temporary container:": "Disky neúspešného pokusu sa nepodarilo odstrániť a zostávajú v dočasnom kontajneri:", "The domain must resolve to the public address of this network before the certificate can be issued.": "Pred vydaním certifikátu musí doména smerovať na verejnú adresu tejto siete.", - "The download client still needs to be configured.": "Stiahnuť klienta ešte treba nakonfigurovať.", - "The download stopped progressing; cancelling this attempt.": "Sťahovanie zastavilo postup, zrušilo tento pokus.", + "The download client still needs to be configured.": "Klienta na sťahovanie ešte treba nastaviť.", + "The download stopped progressing; cancelling this attempt.": "Sťahovanie prestalo napredovať; tento pokus sa ruší.", "The downloaded image does not match its manifest": "Stiahnutý obrázok nezodpovedá jeho manifestu", - "The downloaded image is corrupt:": "Stiahnutý obrázok je poškodený:", + "The downloaded image is corrupt:": "Stiahnutý obraz je poškodený:", "The dpkg package database is clean.": "Databáza balíkov dpkg je čistá.", "The driver installed but does not drive this GPU.": "Ovládač nainštalovaný, ale nepoháňa tento GPU.", "The dynamic NVIDIA hook is missing": "Dynamický NVIDIA hák chýba", "The dynamic NVIDIA hook is missing or duplicated": "Dynamický NVIDIA hák chýba alebo sa zdvojí", - "The dynamic NVIDIA profile requires an unprivileged LXC": "Dynamický profil NVIDIA requires a unprivileged LXC", - "The dynamic profile does not support static driver mounts": "Dynamický profil nepodporuje statické zapojenia vodiča", + "The dynamic NVIDIA profile requires an unprivileged LXC": "Dynamický profil NVIDIA vyžaduje neprivilegovaný kontajner LXC", + "The dynamic profile does not support static driver mounts": "Dynamický profil nepodporuje statické pripojenia ovládača", "The file does not exist, is empty or is not readable.": "Súbor neexistuje, je prázdny alebo sa nedá čítať.", "The file does not exist:": "Súbor neexistuje:", "The file is not a ProxMenux oci-compose.": "Súbor nie je ProxMenux oci-compose.", "The file is too large to be a Compose file": "Súbor je príliš veľký na súbor Compose", "The file is too large to be an oci-compose": "Súbor je príliš veľký na to, aby bol oci-compose", "The filesystem": "Súborový systém", - "The final cleanup did not complete:": "Konečné vyčistenie nedokončilo:", + "The final cleanup did not complete:": "Záverečné upratovanie sa nedokončilo:", "The following DKMS-managed drivers will now be rebuilt against it so they keep working after reboot:": "Nasledujúce ovládače spravované cez DKMS sa teraz znovu zostavia pre nový kernel, aby fungovali aj po reštarte:", "The following LXC containers have NVIDIA passthrough configured:": "Tieto LXC kontajnery majú nastavený NVIDIA passthrough:", "The following backup paths are kernel-tied and are excluded from the picker to keep the target's boot safe. The operator's own tuning inside these paths (IOMMU cmdline, VFIO IDs, custom quirks) is merged back automatically via kernel-agnostic merge:": "Nasledujúce cesty zálohy sú viazané na kernel a sú vylúčené z výberu, aby zostal štart cieľa bezpečný. Vlastné ladenie správcu v týchto cestách (IOMMU cmdline, VFIO ID, vlastné úpravy) sa automaticky znovu zlúči spôsobom nezávislým od kernelu:", @@ -6502,15 +6565,15 @@ "The host has no IPv4 address on the selected bridge": "Hostiteľ nemá IPv4 adresu na vybranom moste", "The host loses access to them. If the group includes a network or storage controller the host needs, cancel at the summary.": "Hostiteľ k nim stratí prístup. Ak skupina obsahuje sieťový alebo úložný radič, ktorý hostiteľ potrebuje, zrušte to v súhrne.", "The host monitor does not see the real host memory": "Monitor hostiteľa nevidí skutočnú pamäť hostiteľa", - "The host monitor does not share this host namespace:": "Monitor hostiteľa nezdieľa tento menový priestor hostiteľa:", + "The host monitor does not share this host namespace:": "Monitor hostiteľa nezdieľa tento menný priestor hostiteľa:", "The host monitor include is unknown or differs from the saved record": "Include monitora hostiteľa je neznámy alebo sa líši od uloženého záznamu", "The host monitor needs consent for privileged access to the host": "Hostiteľský monitor potrebuje súhlas na privilegovaný prístup k hostiteľovi", - "The host monitor profile does not support another sysctl include": "Profil monitora hostiteľa nepodporuje ďalšie sysctl patrí", + "The host monitor profile does not support another sysctl include": "Profil monitora hostiteľa nepodporuje ďalší pripojený konfiguračný súbor sysctl", "The host monitor uses the host network, without DHCP or its own gateway": "Hostiteľský monitor využíva hostiteľskú sieť bez DHCP alebo vlastnej brány", "The host port is already in use:": "Hostiteľský port sa už používa:", "The host-monitor firewall bridge does not match the selected bridge": "Bridge firewallu pre monitor hostiteľa nezodpovedá vybranému bridgeu", "The host-monitor firewall declaration is invalid": "Deklarácia firewallu pre monitor hostiteľa je neplatná", - "The host-monitor firewall port does not match exactly one TCP port in the container contract": "Port firewallu hostiteľa a monitora sa nezhoduje presne s jedným portom TCP v zmluve o kontajneri", + "The host-monitor firewall port does not match exactly one TCP port in the container contract": "Port firewallu monitora hostiteľa sa nezhoduje s práve jedným portom TCP v definícii kontajnera", "The host-monitor firewall port is invalid": "Port firewallu pre monitor hostiteľa je neplatný", "The host-monitor firewall port is not declared as the web port": "Port firewallu pre monitor hostiteľa nie je deklarovaný ako webový port", "The host-monitor firewall port is not declared by this profile": "Port firewallu pre monitor hostiteľa nie je deklarovaný týmto profilom", @@ -6518,15 +6581,15 @@ "The host-monitor web interface uses the host network.": "Webové rozhranie monitora hostiteľa používa sieť hostiteľa.", "The identity of a member was replaced": "Identita člena bola nahradená", "The image cache path is unsafe. Check the template storage before trying again.": "Cesta k vyrovnávacej pamäti obrazov nie je bezpečná. Pred ďalším pokusom skontrolujte úložisko šablón.", - "The image changes the user expected by the adapter": "Obrázok mení používateľa očakávaný adaptérom", + "The image changes the user expected by the adapter": "Obraz mení používateľa očakávaného adaptérom", "The image could not be downloaded or verified. Check registry and storage access, then try again.": "Obraz sa nepodarilo stiahnuť alebo overiť. Skontrolujte prístup k registru a úložisku a skúste to znova.", "The image could not be read from its registry:": "Obraz sa nepodarilo načítať z jeho registra:", - "The image declares data paths that are still stored in the rootfs": "Obrázok deklaruje dátové cesty, ktoré sú stále uložené v rootfs", - "The image did not grant the application user access to the devices; check its native init. Host permissions were not relaxed.": "Obrázok neumožňoval používateľovi aplikácie prístup k zariadeniam; skontrolujte jeho rodený init. Povolenie hostiteľa nebolo uvoľnené.", - "The image did not pass the integrity check": "Obrázok neprešiel kontrolou integrity", + "The image declares data paths that are still stored in the rootfs": "Obraz deklaruje dátové cesty, ktoré sú stále uložené v koreňovom súborovom systéme", + "The image did not grant the application user access to the devices; check its native init. Host permissions were not relaxed.": "Obraz nesprístupnil zariadenia používateľovi aplikácie; skontrolujte jeho natívne spúšťanie. Oprávnenia hostiteľa sa neuvoľnili.", + "The image did not pass the integrity check": "Obraz neprešiel kontrolou integrity", "The image documentation describes them:": "Popis nájdete v dokumentácii obrazu:", "The image does not declare support for this architecture:": "Obraz nevyhlasuje podporu tejto architektúry:", - "The image download did not complete correctly; downloading it again...": "Sťahovanie obrázku nebolo správne dokončené; sťahovanie...", + "The image download did not complete correctly; downloading it again...": "Sťahovanie obrazu sa nedokončilo správne; sťahuje sa znova...", "The image expects files that are given to it one by one:": "Obrázok očakáva súbory, ktoré sú mu dané jeden po druhom:", "The image is already up to date; nothing was changed.": "Obraz je už aktuálny, nič sa nezmenilo.", "The image is in its registry, for one architecture.": "Obraz je vo svojom registri, pre jednu architektúru.", @@ -6534,7 +6597,7 @@ "The image is in its registry:": "Obraz je dostupný vo svojom registri:", "The image manifest failed integrity verification. Try again or contact the image publisher.": "Overenie integrity manifestu obrazu zlyhalo. Skúste to znova alebo kontaktujte vydavateľa obrazu.", "The image or tag was not found on {registry}, or this account cannot access it. Check the reference.": "Obraz alebo tag sa v registri {registry} nenašiel alebo k nemu tento účet nemá prístup. Skontrolujte odkaz na obraz.", - "The image requests NVIDIA, but the host has no working NVIDIA driver": "Obrázok požaduje NVIDIA, ale hostiteľ nemá žiadny funkčný ovládač NVIDIA", + "The image requests NVIDIA, but the host has no working NVIDIA driver": "Obraz požaduje NVIDIA, ale hostiteľ nemá funkčný ovládač NVIDIA", "The image requests disabling part of the AppArmor or seccomp confinement. Continue only if you trust the image and accept this risk.": "Obraz požaduje vypnutie časti obmedzení AppArmor alebo seccomp. Pokračujte iba vtedy, ak obrazu dôverujete a riziko prijímate.", "The image requests disabling part of the AppArmor or seccomp confinement. The Compose offers an optional AppArmor or seccomp relaxation; it will stay disabled unless the user selects it. Continue only if you trust the image and accept this risk.": "Obraz požaduje vypnutie časti obmedzení AppArmor alebo seccomp. Súbor Compose ponúka ich voliteľné uvoľnenie, ktoré zostane vypnuté, kým ho používateľ nevyberie. Pokračujte iba vtedy, ak obrazu dôverujete a riziko prijímate.", "The image was not found in its registry, or it is private:": "Obrázok nebol nájdený v jeho registri, alebo je súkromný:", @@ -6548,15 +6611,15 @@ "The installation runs on the Proxmox node itself, as root": "Inštalácia beží priamo na uzle Proxmox pod používateľom root", "The installation stopped because of an unexpected error": "Inštalácia zastavená kvôli neočakávanej chybe", "The installation/changes require a server restart to apply correctly. Do you want to reboot now?": "Aby sa inštalácia alebo zmeny správne použili, server treba reštartovať. Chcete reštartovať teraz?", - "The installer must run as root on Proxmox VE": "Inštalatér musí spustiť ako koreň na Proxmox VE", + "The installer must run as root on Proxmox VE": "Inštalátor musí bežať pod používateľom root na Proxmox VE", "The instance changed while it was being edited; configure Recreate again": "Inštancia sa zmenila počas editácie; znovu nakonfigurujte Recreate", "The instance changed while it was being edited; open Modify again": "Inštancia sa zmenila počas úpravy; znova otvorte Upraviť", - "The instance does not use NVIDIA": "Tento prípad nepoužíva NVIDIU", - "The instance has a pending operation": "Inštancia má prebiehajúci operation", + "The instance does not use NVIDIA": "Táto inštancia nepoužíva NVIDIA", + "The instance has a pending operation": "Inštancia má nedokončenú operáciu", "The instance identity or status must be reviewed before updating.": "Pred aktualizáciou treba preveriť identitu alebo stav inštancie.", - "The instance is not ready to be updated": "Prípad nie je pripravený na aktualizáciu", - "The instance is not ready; review its pending operation": "Inštancia nie je pripravená; preskúma svoju očakávanú operation", - "The instance record operation did not complete; no container was modified.": "Záznam o inštancii operation sa nedokončil; žiadna nádoba nebola zmenená.", + "The instance is not ready to be updated": "Inštancia nie je pripravená na aktualizáciu", + "The instance is not ready; review its pending operation": "Inštancia nie je pripravená; skontrolujte jej nedokončenú operáciu", + "The instance record operation did not complete; no container was modified.": "Operácia so záznamom inštancie sa nedokončila; žiadny kontajner sa nezmenil.", "The instance registry is not safe": "Inštančný register nie je bezpečný", "The journal belongs to another VMID": "Záznam operácie patrí inému VMID", "The journal belongs to another stack": "Záznam operácie patrí inej aplikácii s viacerými kontajnermi", @@ -6572,35 +6635,35 @@ "The main member must stop first and start last": "Hlavný kontajner sa musí zastaviť ako prvý a spustiť ako posledný", "The main member of the stack is missing": "Chýba hlavný kontajner aplikácie", "The managed host firewall rule could not be removed and was left unchanged.": "Spravované pravidlo firewallu hostiteľa sa nepodarilo odstrániť a zostalo bez zmeny.", - "The manifest does not match its digest": "Manifest nezodpovedá jeho tráveniu", + "The manifest does not match its digest": "Manifest nezodpovedá svojmu identifikátoru digest", "The member journal belongs to another stack operation": "Záznam operácie člena aplikácie patrí k inej operácii celej aplikácie", "The member journal is outside the registry": "Záznam operácie člena aplikácie je mimo registra", "The mode of this job is stop: its guests are shut down during the backup.": "Režim tejto úlohy je stop: jej hostia sú počas zálohovania vypnutí.", - "The mount evidence does not match the verified directories": "Pripojiť dôkazy nezodpovedá overených adresárov", + "The mount evidence does not match the verified directories": "Záznam o pripojení nezodpovedá overeným adresárom", "The mount source or options were not kept": "Zdroj alebo možnosti pripojenia neboli zachované", - "The mounted source differs from the configured directory": "Inštalovaný zdroj sa líši od nakonfigurovaného adresára", + "The mounted source differs from the configured directory": "Pripojený zdroj sa líši od nastaveného adresára", "The mounts of the new container do not match the proposal": "Pripojené cesty nového kontajnera nezodpovedajú návrhu", "The name must start with a lowercase letter and use only lowercase letters, digits, - or _": "Názov musí začínať malým písmenom a používať iba malé písmená, číslice, - alebo _", "The native GPU permissions were not kept": "Natívne povolenia GPU neboli ponechané", - "The native unprivileged idmap is required": "Natívne neprivilegované idmapa je required", + "The native unprivileged idmap is required": "Vyžaduje sa natívne mapovanie ID neprivilegovaného kontajnera", "The new SSH key was installed and is now authorized on the server.\nKey file:": "Nový SSH kľúč bol nainštalovaný a je teraz povolený na serveri.\nSúbor kľúča:", "The new SSH key was pushed to the LXC via 'pct exec' on": "Nový SSH kľúč bol odoslaný do LXC cez 'pct exec' na", - "The new Valkey volume contains unexpected data": "Nový Valkey objem obsahuje neočakávané dáta", - "The new container did not pass validation": "Nová nádoba neprešla validáciou", + "The new Valkey volume contains unexpected data": "Nový dátový zväzok Valkey obsahuje neočakávané údaje", + "The new container did not pass validation": "Nový kontajner neprešiel overením", "The new container is not authorized by the operation journal": "Záznam operácie nepovoľuje tento nový kontajner", - "The new image adds a symbolic link in a generated path": "Nový obrázok pridáva symbolické spojenie na generovanej ceste", + "The new image adds a symbolic link in a generated path": "Nový obraz pridáva symbolický odkaz do generovanej cesty", "The new image changes the PostgreSQL major version; the data must be migrated before updating": "Nový obraz mení hlavnú verziu PostgreSQL; pred aktualizáciou je potrebné migrovať údaje", - "The new image could not be installed": "Nový obrázok nemohol byť nainštalovaný", - "The new image could not be installed:": "Nový obrázok sa nepodarilo nainštalovať:", - "The new image does not fit on the storage where images are downloaded:": "Nový image sa nezmestí na úložisko, kam sa sťahujú image:", - "The new image does not keep a required executable": "Nový obrázok neudržuje spustiteľný required", + "The new image could not be installed": "Nový obraz sa nepodarilo nainštalovať", + "The new image could not be installed:": "Nový obraz sa nepodarilo nainštalovať:", + "The new image does not fit on the storage where images are downloaded:": "Nový obraz sa nezmestí na úložisko určené na sťahovanie obrazov:", + "The new image does not keep a required executable": "Nový obraz neobsahuje požadovaný spustiteľný súbor", "The new image is more recent than the minimum age set for scheduled updates; it is not installed yet.": "Nový obraz zatiaľ nedosiahol minimálny vek nastavený pre plánované aktualizácie, preto sa ešte nenainštaluje.", "The new image requires additional persistent paths": "Nový obraz vyžaduje ďalšie trvalé dátové adresáre", "The new image requires additional persistent paths; use Modify": "Nový obraz vyžaduje ďalšie trvalé cesty; použite Upraviť", "The new image requires additional persistent paths; use Recreate": "Nový obrázok requires ďalšie pretrvávajúce cesty; používať Recreate", "The new prompt will be used in new terminal sessions.": "Nový príkazový riadok sa použije v nových reláciách terminálu.", "The next visit to the dashboard will show the initial setup wizard.": "Pri ďalšom otvorení dashboardu sa zobrazí úvodný sprievodca nastavením.", - "The observed inventory differs from the validated runtime": "Pozorovaný inventár sa líši od overeného času", + "The observed inventory differs from the validated runtime": "Zistený zoznam zariadení sa líši od overeného prostredia NVIDIA", "The oci-compose has errors:": "oci-compose obsahuje chyby:", "The oci-compose is incomplete: it does not say which image to install.": "oci-compose je neúplný: nehovorí, ktorý obraz sa má nainštalovať.", "The oci-compose is not valid JSON:": "oci-compose nie je platný JSON:", @@ -6608,12 +6671,12 @@ "The official inventory contains no NVIDIA devices": "Oficiálny inventár neobsahuje žiadne zariadenia NVIDIA", "The official inventory contains no NVIDIA driver components": "Oficiálny inventár neobsahuje žiadne komponenty ovládača NVIDIA", "The official startup cannot be reproduced": "Oficiálny štart nie je možné reprodukovať", - "The official startup of the application is missing": "Chýba oficiálny začiatok aplikácie", + "The official startup of the application is missing": "Chýba oficiálny spôsob spustenia aplikácie", "The operation already finished; it is not restored automatically": "Operácia už bola dokončená; jej predchádzajúci stav sa automaticky neobnoví", "The operation could not be completed": "Operáciu sa nepodarilo dokončiť", "The operation stopped halfway. Choose \"Recover\" for this container in the OCI management menu to restore the previous installation.": "Operácia sa zastavila v polovici. Ak chcete obnoviť predchádzajúcu inštaláciu, v ponuke Správca OCI aplikácií pri tomto kontajneri vyberte \"Obnoviť\".", "The operation stopped halfway. Choose \"{choice}\" for this container in the OCI management menu.": "Operácia sa zastavila v polovici. V ponuke Správca OCI aplikácií pri tomto kontajneri vyberte \"{choice}\".", - "The operation was stopped because a shared directory changed its identity:": "operation bol zastavený, pretože zdieľaný adresár zmenil svoju identitu:", + "The operation was stopped because a shared directory changed its identity:": "Operácia bola zastavená, pretože zdieľaný adresár zmenil svoju identitu:", "The original MOTD backup is unavailable; no changes were made": "Pôvodná záloha MOTD nie je k dispozícii;neboli vykonané žiadne zmeny", "The original MOTD configuration has been restored": "Pôvodná konfigurácia MOTD bola obnovená", "The original MOTD state is unavailable; no changes were made": "Pôvodný stav MOTD nie je k dispozícii;neboli vykonané žiadne zmeny", @@ -6621,53 +6684,53 @@ "The original rpcbind state could not be restored completely": "Pôvodný stav rpcbind nebolo možné úplne obnoviť", "The original rpcbind state is unavailable; no service state was changed": "Pôvodný stav rpcbind nie je k dispozícii;žiadny servisný stav sa nezmenil", "The package is currently in a broken state and is blocking apt updates on this system.": "Balík je momentálne v nefunkčnom stave a blokuje aktualizácie apt v tomto systéme.", - "The parent of an NVIDIA destination is not a directory": "Materská destinácia NVIDIA nie je adresár", - "The parent of the target is not a directory:": "Materský cieľ nie je adresár:", + "The parent of an NVIDIA destination is not a directory": "Nadradená cesta cieľa NVIDIA nie je adresár", + "The parent of the target is not a directory:": "Nadradená cesta cieľa nie je adresár:", "The password could not be retrieved automatically": "Heslo nebolo možné získať automaticky", "The passwords do not match. Please try again.": "Heslá sa nezhodujú. Skúste to znova.", - "The path escapes the rootfs:": "Cesta uniká koreňom:", + "The path escapes the rootfs:": "Cesta smeruje mimo koreňového súborového systému:", "The path must be absolute and normalized": "Cesta musí byť absolútna a normalizovaná", "The path overlaps an existing mount": "Trasa sa prekrýva s existujúcou montážou", "The path to remove is not mounted:": "Cesta na odstránenie nie je pripojená:", "The persistent NVIDIA hook does not match the installer:": "Trvalý NVIDIA hák nezodpovedá inštalátorovi:", - "The persistent WebUI credentials were not found": "Trvalé WebUI credentials neboli nájdené", + "The persistent WebUI credentials were not found": "Trvalé prihlasovacie údaje webového rozhrania sa nenašli", "The physical NVIDIA selection changed": "Fyzický výber NVIDIA sa zmenil", "The post-start configuration cannot be applied with the LXC stopped": "Nastavenia po spustení nemožno použiť, keď je kontajner LXC zastavený", - "The postgres user was not found in the image": "Užívateľ postgres nebol nájdený v obrázku", - "The prepared directory escapes the rootfs:": "Pripravený adresár unikne koreňom:", + "The postgres user was not found in the image": "Používateľ postgres sa v obraze nenašiel", + "The prepared directory escapes the rootfs:": "Pripravený adresár smeruje mimo koreňového súborového systému:", "The preselected VMID does not exist on this host:": "Predvybrané VMID na tomto hostovi neexistuje:", "The previous native backup will be restored. Shared host directories are not reverted. Displaced disks are kept.": "Predchádzajúce domáce zálohy budú obnovené. Spoločné adresáre sa nevrátia. Zadržiavajú sa vypustené disky.", "The previous native backup will be restored. Shared host directories are not reverted. The disks of the failed attempt are removed.": "Obnoví sa predchádzajúca natívna záloha. Zdieľané hostiteľské adresáre sa nevrátia späť. Disky neúspešného pokusu sa odstránia.", "The previous recognition choice could not be put back:": "Pôvodné nastavenie CPU/GPU pre rozpoznávanie sa nepodarilo obnoviť:", - "The previous stack contract is not safe; review it before reusing it": "Predchádzajúca stack zmluva nie je bezpečná; pred opätovným použitím ju preskúmajte", + "The previous stack contract is not safe; review it before reusing it": "Predchádzajúca definícia zostavy nie je bezpečná; pred opätovným použitím ju skontrolujte", "The previous stack contract is not valid; it is not archived automatically": "Predchádzajúca zmluva nie je platná; nie je automaticky archivovaná", - "The previous stack contract still has containers or VMs:": "Predchádzajúca stack zmluva má stále kontajnery alebo VM:", + "The previous stack contract still has containers or VMs:": "K predchádzajúcej definícii zostavy stále patria kontajnery alebo VM:", "The private address is already assigned to another container:": "Súkromná adresa je už pridelená k inému kontajneru:", "The private bridge does not have the expected address:": "Súkromný most nemá očakávanú adresu:", "The private journal has an unsafe owner or permissions": "Súkromný záznam operácie má nebezpečného vlastníka alebo prístupové práva", - "The private network allocator was not found": "Private Network alocator nebol nájdený", + "The private network allocator was not found": "Nástroj na prideľovanie súkromných sietí sa nenašiel", "The private network is still used by another container and is kept:": "Súkromná sieť sa stále používa v inej nádobe a udržiava sa:", "The private network is still used by another guest and is kept:": "Súkromnú sieť stále používa iná VM alebo iný CT a zostáva zachovaná:", "The private network must be assigned automatically": "Súkromná sieť musí byť automaticky pridelená", "The private network of the application is already in use on this host:": "Súkromná sieť aplikácie sa už na tomto hostiteľovi používa:", "The privilege mode of the container does not match the saved record": "Režim oprávnení kontajnera sa nezhoduje s uloženým záznamom", - "The privileged deployment does not include the required explicit consent": "Privilegované nasadenie nezahŕňa required výslovný súhlas", - "The prlimit soft value exceeds the hard value": "Prlimit soft hodnota presahuje tvrdú hodnotu", + "The privileged deployment does not include the required explicit consent": "Privilegované nasadenie nemá požadovaný výslovný súhlas", + "The prlimit soft value exceeds the hard value": "Mäkký limit prlimit prekračuje pevný limit", "The profile sets SETUP_PORT=8123. Port 80 is only used when that variable is emptied.": "Profil nastavuje SETUP_PORT=8123. Port 80 sa používa iba vtedy, keď je táto premenná prázdna.", "The proposal changes the identity of the instance": "Návrh mení totožnosť inštancie", "The proposed ARC maximum is below Proxmox VE's pool-size guideline:": "Navrhované maximum ARC je nižšie ako smernica Proxmox VE pre veľkosť bazéna:", "The proposed changes no longer match; nothing was adopted": "Navrhované zmeny sa už nezhodujú;nič nebolo prijaté", - "The published views must be inside the common root": "Publikované názory musia byť vo vnútri spoločného koreňa", + "The published views must be inside the common root": "Sprístupnené pohľady musia byť v spoločnom koreňovom adresári", "The read-only view does not apply the expected protection": "Pohľad len na čítanie neuplatňuje očakávanú ochranu", - "The read-only view was not published": "Zobrazenie nebolo zverejnené", + "The read-only view was not published": "Pohľad iba na čítanie sa nesprístupnil", "The read/write view was not published": "Pohľad na čítanie/ zápis nebol zverejnený", "The recipe requires configuration at startup; its coordinated replay is not available": "Konfigurácia vyžaduje úpravy pri spustení, ktoré koordinovaná aktualizácia zatiaľ nevie zopakovať", "The recognition change of Immich did not complete:": "Zmenu CPU/GPU pre rozpoznávanie v Immichu sa nepodarilo dokončiť:", "The recognition of Immich was not changed:": "Spôsob rozpoznávania v Immichu sa nezmenil:", "The record belongs to another container": "Záznam patrí inému kontajneru", "The record does not belong to this operation": "Záznam nepatrí k tejto operácii", - "The record no longer belongs to this operation": "Záznam už nepatrí k tomuto operation", - "The record of a member was replaced; the assembly is not resumed": "Záznam o členovi bol nahradený; zhromaždenie sa neobnovuje", + "The record no longer belongs to this operation": "Záznam už nepatrí tejto operácii", + "The record of a member was replaced; the assembly is not resumed": "Záznam kontajnera bol nahradený; zostavovanie nebude pokračovať", "The record or diagnosis could not be completed; no update was run.": "Záznam alebo diagnóza nemohla byť dokončená; žiadna aktualizácia nebola spustená.", "The recovery did not complete. Review the log and choose \"Recover\" again for this container in the OCI management menu.": "Obnova sa nedokončila. Skontrolujte záznam a v ponuke Správca OCI aplikácií pri tomto kontajneri znova vyberte \"Obnoviť\".", "The recovery did not complete. Review the log and choose \"{choice}\" again for this container in the OCI management menu.": "Obnova sa nedokončila. Skontrolujte záznam a v ponuke Správca OCI aplikácií pri tomto kontajneri znova vyberte \"{choice}\".", @@ -6677,11 +6740,11 @@ "The registry authentication path cannot contain symbolic links.": "Cesta k súboru s prihlasovacími údajmi registrov nesmie obsahovať symbolické odkazy.", "The registry authentication path has an unsafe parent directory.": "Nadradený adresár súboru s prihlasovacími údajmi registrov nie je bezpečný.", "The registry credential directory must be owned by root with mode 700.": "Adresár s prihlasovacími údajmi registrov musí vlastniť root a musí mať oprávnenia 700.", - "The registry did not return the saved image digest": "Register nevrátil uložený image digest", + "The registry did not return the saved image digest": "Register nevrátil uložený identifikátor digest obrazu", "The registry returned incomplete or unsupported image metadata.": "Register vrátil neúplné alebo nepodporované metadáta obrazu.", "The registry returned invalid or unsupported image metadata.": "Register vrátil neplatné alebo nepodporované metadáta obrazu.", - "The remote does not exist; create and authorize it first in the WebUI:": "Vzdialenosť neexistuje; vytvoriť a schváliť ju ako prvý na WebUI:", - "The remote installer must run as root on Proxmox VE": "Diaľkový inštalátor musí bežať ako koreň na Proxmox VE", + "The remote does not exist; create and authorize it first in the WebUI:": "Vzdialené úložisko neexistuje; najprv ho vytvorte a autorizujte vo webovom rozhraní:", + "The remote installer must run as root on Proxmox VE": "Vzdialený inštalátor musí bežať pod používateľom root na Proxmox VE", "The remote must already be created and authorized in the Rclone web UI. This operation restarts the CT and publishes two FUSE views on the host.": "Vzdialený musí byť už vytvorený a schválený v Rclone web UI. Tento operation reštartuje CT a zverejňuje dva pohľady FUSE na hostiteľa.", "The remote path must be relative and cannot contain line breaks": "Cesta vo vzdialenom úložisku musí byť relatívna a nesmie obsahovať zalomenia riadkov", "The removal could not be prepared:": "Odstránenie nebolo možné pripraviť:", @@ -6689,15 +6752,15 @@ "The render device does not exist:": "Renderovacie zariadenie neexistuje:", "The repair must preserve the image dependencies:": "Oprava musí zachovať závislosť obrazu:", "The requested VMID block is already in use": "Požadovaný blok VMID sa už používa", - "The requested machine learning GPU profile is not working; it is not replaced by CPU": "Požadovaný profil strojového učenia sa GPU nefunguje; nie je nahradený CPU", + "The requested machine learning GPU profile is not working; it is not replaced by CPU": "Požadovaný profil GPU pre strojové učenie nefunguje; nenahradí sa CPU", "The resource edit could not be fully rolled back. Open Manage again to review and import the current Proxmox settings.": "Zmenu prostriedkov sa nepodarilo úplne vrátiť späť. Znova otvorte správu aplikácií, skontrolujte aktuálne nastavenia systému Proxmox a prevezmite ich.", "The restored service did not pass its health check": "Obnovená služba neprešla kontrolou stavu", "The restored service stopped; the recovery is not confirmed": "Obnovená služba sa zastavila; obnova sa nepotvrdila", "The reviewed Tandoor stack does not require a privileged LXC.": "Overená zostava Tandoor nevyžaduje privilegovaný kontajner LXC.", "The root password is asked for when connecting from another machine. Inside the container, the mariadb command opens as root without a password.": "Heslo používateľa root sa vyžaduje pri pripojení z iného počítača. Vnútri kontajnera sa príkaz mariadb otvorí ako root bez hesla.", - "The rootfs capture only belongs to the running installation": "Rootfs capture patrí len do bežiacej inštalácie", + "The rootfs capture only belongs to the running installation": "Zachytenie koreňového súborového systému patrí iba prebiehajúcej inštalácii", "The rootfs is not managed by Proxmox": "Rootfs nie je riadený Proxmox", - "The rootfs is not mounted": "Korene nie sú namontované", + "The rootfs is not mounted": "Koreňový súborový systém nie je pripojený", "The same GPU cannot be used by two VMs at the same time.": "Rovnakú GPU nemôžu používať dve VM naraz.", "The same connection can be given as container variables instead of the file: UN_SONARR_0_URL and UN_SONARR_0_API_KEY, or the UN_RADARR_0_ equivalents.": "Namiesto súboru možno pripojenie nastaviť premennými kontajnera: UN_SONARR_0_URL a UN_SONARR_0_API_KEY alebo zodpovedajúcimi premennými UN_RADARR_0_.", "The saved MOTD state is invalid; no changes were made": "Uložený stav MOTD je neplatný;neboli vykonané žiadne zmeny", @@ -6741,8 +6804,8 @@ "The selected configuration needs to start the LXC during the installation": "Vybraná konfigurácia musí spustiť LXC počas inštalácie", "The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Vybraný kontajner je neprivilegovaný. Na priame priradenie zariadenia je potrebný privilegovaný kontajner.", "The selected device": "Vybrané zariadenie", - "The selected device is not a block device": "Vybrané zariadenie nie je blokovacie zariadenie", - "The selected device is not a character device": "Vybrané zariadenie nie je charakterové zariadenie", + "The selected device is not a block device": "Vybrané zariadenie nie je blokové zariadenie", + "The selected device is not a character device": "Vybrané zariadenie nie je znakové zariadenie", "The selected directory does not exist:": "Vybraný priečinok neexistuje:", "The selected disk has an active swap partition. Aborting.": "Vybraný disk má aktívny swap oddiel. Akcia sa ruší.", "The selected disk is currently used by a RUNNING VM or CT. Stop it before formatting.": "Vybraný disk práve používa BEŽIACA VM alebo CT. Pred formátovaním ju zastavte.", @@ -6770,34 +6833,34 @@ "The source VM also has these audio devices, likely added together with the GPU. Remove them too?": "Zdrojová VM má aj tieto audio zariadenia, pravdepodobne pridané spolu s GPU. Odstrániť aj tie?", "The specified directory does not exist:": "Zadaný priečinok neexistuje:", "The stability period must be shorter than the healthcheck timeout": "Obdobie stability musí byť kratšie ako časový limit pre kontrolu zdravotného stavu.", - "The stack contains devices or directives without a translation": "Stock obsahuje zariadenia alebo smernice bez prekladu", + "The stack contains devices or directives without a translation": "Zostava obsahuje zariadenia alebo direktívy bez podporovaného prevodu", "The stack does not have the expected native hook": "Aplikácia nemá očakávaný natívny spúšťací skript", "The stack journal is outside the registry": "Záznam operácie celej aplikácie je mimo registra", - "The stack member has no declared adaptation profile": "Stack člen nemá deklarovaný adaptačný profil", + "The stack member has no declared adaptation profile": "Kontajner zostavy nemá deklarovaný adaptačný profil", "The stack name only accepts lowercase letters, numbers and hyphens": "Názov zostavy môže obsahovať iba malé písmená, číslice a pomlčky", "The stack needs member adaptations or a verification of missing volumes": "Aplikácia vyžaduje úpravy svojich kontajnerov alebo overenie chýbajúcich dátových zväzkov", "The stack operation had already finished": "Operácia celej aplikácie už bola dokončená", "The stack operation has not finished yet": "Operácia celej aplikácie ešte nebola dokončená", "The stack operation stopped halfway. Select the stack again in the OCI management menu to recover it.": "Operácia celej aplikácie zostala nedokončená. Ak ju chcete obnoviť, v ponuke správy OCI znova vyberte danú aplikáciu.", - "The stack registry is incomplete; review the private contracts.": "Register stoh je neúplný; preskúma súkromné zmluvy.", - "The stack startup hook was not found": "Stack startup hák nebol nájdený", + "The stack registry is incomplete; review the private contracts.": "Register zostavy je neúplný; skontrolujte súkromné definície.", + "The stack startup hook was not found": "Spúšťací skript zostavy sa nenašiel", "The stack update was saved.": "Aktualizácia celej aplikácie bola uložená.", "The startup differs from the declared Nextcloud adapter": "Spustenie sa líši od deklarovaného Nextcloud adaptéra", "The startup differs from the declared adapter": "Spustenie sa líši od deklarovaného adaptéra", - "The staticfiles volume needs at least 1 GB": "Objem statických súborov potrebuje aspoň 1 GB", + "The staticfiles volume needs at least 1 GB": "Dátový zväzok staticfiles musí mať najmenej 1 GB", "The storage chosen for this backup cannot be written:": "Do úložiska vybratého pre túto zálohu sa nedá zapisovať:", "The storage chosen for this backup has no room either:": "Ani na úložisku vybratom pre túto zálohu nie je dostatok miesta:", "The storage does not accept backups:": "Úložisko neprijíma zálohy:", "The storage has been removed and the disk unmounted.": "Úložisko bolo odstránené a disk odpojený.", "The storage has less than 40 GB free for the ROCm image.": "Na úložisku je menej než 40 GB voľného miesta pre obraz ROCm.", "The sysctl content was modified outside the saved record": "Obsah sysctl bol upravený mimo uloženého záznamu", - "The sysctl include is a link:": "Sysctl patrí je odkaz:", - "The sysctl include is not a safe host file": "Sysctl patrí nie je bezpečný hostiteľský súbor", - "The sysctl include is not restored over a symbolic link": "Sysctl patrí nie je obnovený cez symbolické spojenie", - "The sysctl include is unknown or differs from the saved record": "Sysctl patrí nie je známe alebo sa líši od uloženého záznamu", + "The sysctl include is a link:": "Pripojený konfiguračný súbor sysctl je odkaz:", + "The sysctl include is not a safe host file": "Pripojený konfiguračný súbor sysctl nie je bezpečný súbor hostiteľa", + "The sysctl include is not restored over a symbolic link": "Konfiguračný súbor sysctl sa neobnovuje cez symbolický odkaz", + "The sysctl include is unknown or differs from the saved record": "Pripojený konfiguračný súbor sysctl je neznámy alebo sa líši od uloženého záznamu", "The temporary password could not be retrieved.": "Dočasné heslo nebolo možné získať späť.", "The test will continue even if you close this terminal.": "Test bude pokračovať aj po zatvorení tohto terminálu.", - "The tmpfs mounts of the container differ from the saved record": "Tmpfs držiaky kontajnera sa líšia od uloženého záznamu", + "The tmpfs mounts of the container differ from the saved record": "Pripojenia tmpfs v kontajneri sa líšia od uloženého záznamu", "The tmpfs path or size is outside the supported profile": "Tmpfs cesta alebo veľkosť je mimo podporovaný profil", "The translated recipe changed during the preparation": "Preložený recept sa počas prípravy zmenil", "The update check could not be completed. Try again later.": "Kontrolu aktualizácií sa nepodarilo dokončiť. Skúste to znova neskôr.", @@ -6805,7 +6868,7 @@ "The value must be a number:": "Hodnota musí byť číslo:", "The values do not match. Enter them again.": "Hodnoty sa nezhodujú. Zadajte ich znova.", "The variable contains control characters:": "Premenná obsahuje kontrolné znaky:", - "The variable contains line breaks:": "Premenná obsahuje prestávky na riadku:", + "The variable contains line breaks:": "Premenná obsahuje zalomenia riadkov:", "The vfio.conf entries have been removed and initramfs rebuilt.": "Položky vfio.conf boli odstránené a initramfs prebudované.", "The volume ID does not contain a disk name for this CT; automatic adoption is unsafe": "ID zväzku neobsahuje názov disku tohto CT; automatické prevzatie nie je bezpečné", "The watchdog could not be enabled. Turn it on from Manage installed OCI applications.": "Watchdog sa nepodarilo povoliť. Zapnite ho v Spravovať nainštalované aplikácie OCI.", @@ -6826,7 +6889,7 @@ "There is no temporary container of this operation to keep the current disks": "Chýba dočasný kontajner tejto operácie na zachovanie aktuálnych diskov", "There is no verified backup; a modified container is not touched": "Chýba overená záloha; zmenený kontajner zostáva nedotknutý", "These Proxmox resources were added outside ProxMenux:": "Tieto zdroje Proxmox boli pridané mimo ProxMenux:", - "These VMIDs are not free:": "Tieto VMID nie sú zadarmo:", + "These VMIDs are not free:": "Tieto VMID nie sú voľné:", "These are the changes that will be made": "Tieto zmeny sa vykonajú", "These changes were made in Proxmox outside ProxMenux:": "Tieto zmeny boli vykonané v Proxmox mimo ProxMenux:", "These container mount paths have an extension-like suffix:": "Tieto cesty pripojenia v kontajneri majú príponu podobnú koncovke súboru:", @@ -6861,7 +6924,7 @@ "This backup is encrypted.": "Táto záloha je šifrovaná.", "This backup was taken on kernel": "Táto záloha bola vytvorená na kerneli", "This cleanup will:": "Toto čistenie:", - "This container belongs to a stack; publish the whole stack": "Tento kontajner patrí do stohu; publikovať celý zásobník", + "This container belongs to a stack; publish the whole stack": "Tento kontajner patrí do zostavy; sprístupnite celú zostavu", "This container belongs to a stack; recover the whole stack": "Tento kontajner patrí do aplikácie s viacerými kontajnermi; obnovte celú aplikáciu", "This container does not have apt-get. NFS client installation only supports Debian/Ubuntu containers.": "Tento kontajner nemá apt-get. Inštalácia NFS klienta je podporovaná iba v Debian/Ubuntu kontajneroch.", "This container does not have apt-get. Samba client installation only supports Debian/Ubuntu containers.": "Tento kontajner nemá apt-get. Inštalácia Samba klienta je podporovaná iba v Debian/Ubuntu kontajneroch.", @@ -6943,7 +7006,7 @@ "This template requests the host PID namespace, which has no validated safe LXC translation yet": "Táto šablóna požaduje menný priestor PID hostiteľa, ktorý zatiaľ nemá overený bezpečný prevod do LXC", "This tool is designed for systems with AMD GPUs.": "Tento nástroj je určený pre systémy s AMD GPU.", "This tool is designed for systems with Intel GPUs.": "Tento nástroj je určený pre systémy s Intel GPU.", - "This translator only supports the Nextcloud stack": "Tento prekladateľ podporuje iba Nextcloud stack", + "This translator only supports the Nextcloud stack": "Tento nástroj na prevod podporuje iba zostavu Nextcloud", "This value is required.": "Táto hodnota je povinná.", "This variant requires the device": "Tento variant vyžaduje zariadenie", "This version does not build against the running kernel.": "Táto verzia nie je postavená proti bežiacemu jadru.", @@ -7064,7 +7127,7 @@ "Unapplied changes": "Nepoužité zmeny", "Understand the security implications of privileged containers": "Uistite sa, že rozumiete bezpečnostným dôsledkom privilegovaných kontajnerov", "Unexpected Proxmox inventory; recovery blocked": "Neočakávaný inventár Proxmox; zablokovaná obnova", - "Unexpected formatting directory in the new Valkey volume": "Neočakávaný formátovanie adresára v novom hlasitosti Valkey", + "Unexpected formatting directory in the new Valkey volume": "Neočakávaný adresár vytvorený formátovaním v novom dátovom zväzku Valkey", "Ungoogled Chromium is Google Chromium, sans dependency on Google web services.": "Ungoogled Chromium je Chromium bez závislosti od webových služieb Google.", "Unified LLM Fine-Tuning with 100+ Models": "Jednotné dolaďovanie viac ako 100 jazykových modelov LLM", "Uninstall Coral drivers and configuration": "Odinštalovať ovládače a nastavenie Coral", @@ -7118,7 +7181,7 @@ "Unpackerr configured": "Konfigurovaný Unpackerr", "Unpackerr has no web interface and extracts nothing until it is pointed at a Starr application. Uncomment the [sonarr.0] or [radarr.0] section in /config/unpackerr.conf inside the container, set its url and api_key, then restart the container.": "Unpackerr nemá webové rozhranie a nerozbaľuje nič, kým ho neprepojíte s aplikáciou Starr. V /config/unpackerr.conf v kontajneri odkomentujte sekciu [sonarr.0] alebo [radarr.0], nastavte url a api_key a kontajner reštartujte.", "Unpackerr requires Sonarr, Radarr or Lidarr in this suite": "Unpackerr v tejto zostave vyžaduje Sonarr, Radarr alebo Lidarr", - "Unpackerr stopped during its first start": "Unpackerr zastavil počas svojho prvého štartu", + "Unpackerr stopped during its first start": "Unpackerr sa počas prvého spustenia zastavil", "Unprivileged": "Neprivilegovaný", "Unprivileged Container Access": "Prístup neprivilegovaného kontajnera", "Unprivileged container": "Neprivilegovaný kontajner", @@ -7134,7 +7197,7 @@ "Unrecognized host monitor profile": "Nerozpoznaný profil monitora hostiteľa", "Unrecognized native configuration": "Nerozpoznaná natívna konfigurácia", "Unrecognized qBittorrent configuration format": "Nerozpoznaný formát konfigurácie qBittorrent", - "Unrecognized stack adapter or role": "Nerozpoznaný stack adaptér alebo role", + "Unrecognized stack adapter or role": "Nerozpoznaný adaptér alebo úloha kontajnera v zostave", "Unrecognized stack adapter:": "Nerozpoznaný adaptér aplikácie s viacerými kontajnermi:", "Unrecognized stack structure:": "Nerozpoznaná štruktúra aplikácie s viacerými kontajnermi:", "Unrecognized volume definition": "Nerozpoznaná definícia dátového zväzku", @@ -7143,7 +7206,7 @@ "Unsafe dependency contract": "Nebezpečné nastavenie závislostí", "Unsafe dependency hook contract": "Nebezpečné nastavenie spúšťacieho skriptu závislostí", "Unsafe instance directory": "Nebezpečný priečinok inštancie", - "Unsafe instance record": "Nebezpečný záznam o udalosti", + "Unsafe instance record": "Nebezpečný záznam inštancie", "Unsafe journal or lock file": "Nebezpečný súbor denníka alebo zámku", "Unsafe private configuration path": "Nebezpečná konfiguračná dráha", "Unsafe qBittorrent configuration path": "Nebezpečná konfiguračná dráha qBittorrent", @@ -7152,19 +7215,19 @@ "Unsafe registry directory": "Nebezpečný adresár registrov", "Unsafe registry lock": "Nebezpečný zámok registra", "Unsafe rootfs for the capture": "Nebezpečné rootfs pre zachytenie", - "Unsafe stack assembly": "Nebezpečná montáž zásobníka", - "Unsafe volume path": "Nebezpečná trasa objemu", + "Unsafe stack assembly": "Nebezpečné zostavenie aplikácie", + "Unsafe volume path": "Nebezpečná cesta dátového zväzku", "Unsupported CPU allocation mode:": "Nepodporovaný režim prideľovania CPU:", "Unsupported GID strategy:": "Nepodporovaná stratégia GID:", "Unsupported NVIDIA mode:": "Nepodporovaný režim NVIDIA:", - "Unsupported OCI digest:": "Nepodporovaná digescia OCI:", + "Unsupported OCI digest:": "Nepodporovaný identifikátor digest OCI:", "Unsupported OCI-LXC AppArmor profile:": "Nepodporovaný profil OCI-LXC AppArmor:", "Unsupported OCI-LXC seccomp profile:": "Nepodporovaný profil OCI-LXC seccomp:", "Unsupported Terminal": "Nepodporovaný terminál", "Unsupported architecture for Figurine:": "Nepodporovaná architektúra pre Figurine:", "Unsupported architecture:": "Nepodporovaná architektúra:", "Unsupported backup compression": "Nepodporovaná kompresia zálohy", - "Unsupported credential pattern:": "Nepodporovaný credential vzor:", + "Unsupported credential pattern:": "Nepodporovaný vzor prihlasovacích údajov:", "Unsupported declarative ostype:": "Nepodporovaný deklaratívny typ os:", "Unsupported device GID strategy": "Stratégia GID nepodporovaného zariadenia", "Unsupported device options": "Nepodporované možnosti zariadenia", @@ -7172,13 +7235,13 @@ "Unsupported dynamic NVIDIA capabilities:": "Nepodporované dynamické schopnosti NVIDIA:", "Unsupported format. Only .ova and .ovf files are supported.": "Nepodporovaný formát. Podporované sú iba .ova a .ovf súbory.", "Unsupported kept setting:": "Nepodporované uchované nastavenie:", - "Unsupported media storage mode:": "Nepodporovaný režim pamäťového média:", + "Unsupported media storage mode:": "Nepodporovaný režim úložiska médií:", "Unsupported mount options": "Nepodporované možnosti pripojenia", "Unsupported mount type": "Nepodporovaný typ pripojenia", "Unsupported mount type:": "Nepodporovaný typ pripojenia:", - "Unsupported native device type:": "Nepodporovaný natívne typ zariadenia:", + "Unsupported native device type:": "Nepodporovaný natívny typ zariadenia:", "Unsupported network settings": "Nepodporované sieťové nastavenia", - "Unsupported operation": "Nepodporovaná operation", + "Unsupported operation": "Nepodporovaná operácia", "Unsupported or unknown Proxmox version; no repository changed.": "Nepodporovaná alebo neznáma verzia Proxmox; žiadne úložisko sa nezmenilo.", "Unsupported output format:": "Nepodporovaný výstupný formát:", "Unsupported owner strategy:": "Nepodporovaná stratégia vlastníka:", @@ -7193,9 +7256,9 @@ "Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host and changes nothing otherwise.": "Kým nie sú znova zaregistrované, nemožno ich aktualizovať ani spravovať odtiaľto. Obnova najprv skontroluje, či všetko, čo potrebujú, je na tomto hostiteľovi a inak nič nezmení.", "Until they are registered again they cannot be updated or managed from here. The recovery checks first that everything they need is on this host; an application that lacks something is not registered.": "Kým nie sú znova zaregistrované, nemožno ich aktualizovať ani spravovať odtiaľto. Obnova najprv skontroluje, či všetko, čo potrebujú, je na tomto hostiteľovi; aplikácia, ktorej niečo chýba, nie je zaregistrovaná.", "Until they are registered again they cannot be updated or managed from here. The recovery checks first what prevents their registration; an application with such a problem is not registered. A Rclone mount that has to be enabled again does not prevent it, but then the applications are not started automatically.": "Kým nie sú znova zaregistrované, nemožno ich odtiaľto aktualizovať ani spravovať. Pred opätovnou registráciou sa skontroluje, čo jej bráni. Aplikácia s takýmto problémom sa nezaregistruje. Pripojenie Rclone, ktoré treba znova povoliť, registrácii nebráni, ale aplikácie sa potom nespúšťajú automaticky.", - "Untrusted or modified NVIDIA hook": "Nespoľahlivý alebo modifikovaný háčik NVIDIA", + "Untrusted or modified NVIDIA hook": "Nedôveryhodný alebo zmenený spúšťací skript NVIDIA", "Unused image removed from the cache:": "Nepoužívaný obraz odstránený z vyrovnávacej pamäte:", - "Unused images removed from the cache:": "Nepoužité obrázky odstránené z vyrovnávacej pamäte:", + "Unused images removed from the cache:": "Nepoužívané obrazy odstránené z vyrovnávacej pamäte:", "Update": "Aktualizácia", "Update Available": "Dostupná aktualizácia", "Update Ceph repository (Only if using Ceph):": "Aktualizujte Ceph repozitár (iba ak používate Ceph):", @@ -7221,7 +7284,7 @@ "Update kernel to compatible version": "Aktualizovať kernel na kompatibilnú verziu", "Update now?": "Spustiť aktualizáciu?", "Update package index:": "Aktualizujte index balíkov:", - "Update prepared": "Aktualizácia", + "Update prepared": "Aktualizácia je pripravená", "Update selected OCI applications": "Aktualizovať vybrané aplikácie OCI", "Update system to latest PVE 8.4+ (if not done already):": "Aktualizujte systém na najnovšie PVE 8.4+ (ak to ešte nie je hotové):", "Update the image with the saved configuration": "Aktualizovať obraz s uloženými nastaveniami", @@ -7341,7 +7404,7 @@ "Using advanced configuration": "Používa sa pokročilé nastavenie", "Using default Proxmox logo...": "Používam predvolené logo Proxmoxu...", "Using existing encryption key:": "Používa sa existujúci šifrovací kľúč:", - "Using the image verified by the transaction": "Použitie obrázku overené transakcie", + "Using the image verified by the transaction": "Používa sa obraz overený transakciou", "Using the verified image from the cache": "Použitie overeného obrazu z vyrovnávacej pamäte", "Utilities": "Nástroje", "Utilities Installation Menu": "Menu inštalácie nástrojov", @@ -7481,8 +7544,8 @@ "Volume configuration cancelled": "Nastavenie dátového úložiska bolo zrušené", "Volume options are not yet supported": "Voľby dátových zväzkov zatiaľ nie sú podporované", "Volume size in GB": "Veľkosť dátového disku v GB", - "Volumes attached": "Pripojené objemy", - "Volumes prepared for the first start:": "Objemy pripravené na prvý začiatok:", + "Volumes attached": "Dátové zväzky sú pripojené", + "Volumes prepared for the first start:": "Dátové zväzky pripravené na prvé spustenie:", "Volumes shared between services are not yet supported": "Dátové zväzky zdieľané medzi službami zatiaľ nie sú podporované", "Vscodium-web is a community-driven, freely-licensed binary distribution of the remote host web component of Microsoft's editor VS Code.": "VSCodium Web je komunitne spravovaná binárna distribúcia vzdialenej webovej súčasti editora VS Code od Microsoftu so slobodnou licenciou.", "Vulnerability detection": "hľadania zraniteľností", @@ -7607,7 +7670,7 @@ "Write access verified for user:": "Prístup na zápis bol overený pre používateľa:", "Write the value it produces instead.": "Napíšte namiesto toho hodnotu, ktorú vytvára.", "Wrong SHA-256 in": "Nesprávne SHA-256 v", - "Wrong inherited registry lock": "Nesprávny zdedený register", + "Wrong inherited registry lock": "Nesprávny zdedený zámok registra", "Wrong passphrase": "Nesprávna fráza", "Wrong size in": "Nesprávna veľkosť", "Xbackbone is a simple, self-hosted, lightweight PHP file manager that support the instant sharing tool ShareX and *NIX systems. It supports uploading and displaying images, GIF, video, code, formatted text, and file downloading and uploading. Also have a web UI with multi user management, past uploads history and search support.": "XBackBone je jednoduchý a nenáročný správca súborov v PHP na vlastnom serveri s podporou ShareX a systémov *NIX. Podporuje nahrávanie, zobrazovanie a sťahovanie obrázkov, GIF, videa, kódu a formátovaného textu. Webové rozhranie ponúka správu viacerých používateľov, históriu nahratých súborov a vyhľadávanie.", @@ -7721,7 +7784,7 @@ "apex group still has members; left in place:": "Skupina apex stále má členov, preto zostáva ponechaná:", "apex kernel module not loaded on host. Run \"Install Coral on Host\" first or the container will not see /dev/apex_0.": "Kernel modul apex nie je na hoste načítaný. Najprv spustite \"Install Coral on Host\", inak kontajner neuvidí /dev/apex_0.", "appears to be part of a": "vyzerá, že je súčasťou", - "apply requires the OCI archive of the resolved image": "aplikovať requires archív OCI vyriešeného obrazu", + "apply requires the OCI archive of the resolved image": "Akcia apply vyžaduje archív OCI určeného obrazu", "applying minimal banner patch": "používam minimálnu úpravu bannera", "apt cache refreshed.": "apt cache bola obnovená.", "apt-get exited": "apt-get skončil", @@ -7735,7 +7798,7 @@ "available in this same GPU and TPU menu.": "dostupnej v tomto istom GPU a TPU menu.", "backup at /etc/fstab.proxmenux.bak": "záloha v /etc/fstab.proxmenux.bak", "ban": "zákaz", - "belongs to another OCI installation": "patrí do iného zariadenia OCI", + "belongs to another OCI installation": "patrí do inej inštalácie OCI", "blocking issue(s).": "blokujúcich problémov.", "btrfs — Proxmox dir storage (snapshots, compression)": "btrfs — Proxmox dir úložisko (snapshoty, kompresia)", "btrfs — snapshots and compression": "btrfs - snapshoty a kompresia", @@ -7753,7 +7816,7 @@ "copyparty web interface": "Webové rozhranie copyparty", "could not be compiled for kernel": "nepodarilo sa skompilovať pre jadro", "could not validate NVIDIA; exit code": "nemohol potvrdiť NVIDIA; kód výstupu", - "cpuunits must be between 8 and 10000": "cpujednotky musia byť od 8 do 10000", + "cpuunits must be between 8 and 10000": "Hodnota cpuunits musí byť od 8 do 10000", "custom": "vlastné", "custom dependency commands are not yet supported": "Vlastné príkazy závislosti ešte nie sú podporované", "custom path(s) saved.": "uložených vlastných ciest.", @@ -7782,7 +7845,7 @@ "doplarr_rs starts from the example configuration and connects to nothing. Write the token of your Discord bot in discord_token in /config/config.toml inside the container.": "doplarr_rs začína s ukážkovým nastavením a k ničomu sa nepripája. Token robota Discord zapíšte do discord_token v /config/config.toml v kontajneri.", "downloaded Compose file": "stiahnutého súboru Compose", "dpkg still reports unfinished package work; review": "dpkg stále hlási nedokončenú prácu s balíkom;recenzia", - "driver components": "komponenty vodiča", + "driver components": "súčasti ovládača", "driver:": "ovládač:", "e.g.": "napr.", "empty = generate": "prázdne = generovať", @@ -8053,7 +8116,7 @@ "qBittorrent did not apply the category:": "qBittorrent neuplatnila kategóriu:", "qBittorrent did not apply the download paths": "qBittorrent nepoužila cesty na stiahnutie", "qBittorrent requires a non-empty password": "qBittorrent vyžaduje neprázdne heslo", - "qBittorrent: authenticated access to the preferences could not be verified": "qBittorrent: nemohol sa overiť overený prístup k preferenciám", + "qBittorrent: authenticated access to the preferences could not be verified": "qBittorrent: nepodarilo sa overiť prihlásený prístup k nastaveniam", "qBittorrent: invalid login response or missing session cookie": "qBittorrent: neplatná odpoveď prihlásenia alebo chýbajúci súbor cookie sedenia", "raw USB disk — no filesystem (will be FORMATTED)": "surový USB disk - bez súborového systému (bude NAformátovaný)", "read-only": "iba na čítanie", diff --git a/oci/remote/configure_jellyfin_encoding.py b/oci/remote/configure_jellyfin_encoding.py index a54d6707..a8b3d6e3 100644 --- a/oci/remote/configure_jellyfin_encoding.py +++ b/oci/remote/configure_jellyfin_encoding.py @@ -93,8 +93,8 @@ def main() -> None: rootfs = Path(sys.argv[1]) configuration = json.loads(sys.argv[2]) path, changed = update_encoding(rootfs, configuration) - state = "updated" if changed else "already applied" - print(f"Jellyfin configuration {state}: /{path.relative_to(rootfs.resolve())}") + state = translate("Jellyfin configuration updated:") if changed else translate("Jellyfin configuration already applied:") + print(f"{state} /{path.relative_to(rootfs.resolve())}") if __name__ == "__main__": diff --git a/oci/remote/oci_dhcp_lease.py b/oci/remote/oci_dhcp_lease.py index 92213719..6dd390e0 100644 --- a/oci/remote/oci_dhcp_lease.py +++ b/oci/remote/oci_dhcp_lease.py @@ -20,6 +20,7 @@ import re import subprocess import sys import time +from oci_ui import translate CONFIGS = Path('/etc/pve/lxc') HOOKS = Path('/var/lib/lxc') @@ -121,12 +122,13 @@ def check(vmid, settle=0): except BlockingIOError: return for name, version in repair(vmid, settle): - print(f'CT {vmid}: DHCP client of {name} (IPv{version}) started again', flush=True) + print(translate('CT {vmid}: DHCP client of {name} (IPv{version}) started again').format( + vmid=vmid, name=name, version=version), flush=True) def main(): if len(sys.argv) != 2 or not (sys.argv[1].isdigit() or sys.argv[1] == '--all'): - print('usage: oci_dhcp_lease.py | --all', file=sys.stderr) + print(translate('Usage:') + ' oci_dhcp_lease.py | --all', file=sys.stderr) return 2 LOCKS.mkdir(parents=True, exist_ok=True) if sys.argv[1] == '--all': diff --git a/oci/remote/oci_image_cache.py b/oci/remote/oci_image_cache.py index 34de491d..e8f05490 100644 --- a/oci/remote/oci_image_cache.py +++ b/oci/remote/oci_image_cache.py @@ -10,6 +10,7 @@ import sys import time import oci_instances as instances +from oci_ui import translate # Names given by the OCI installers and by updates; other archives are never touched. NAME = re.compile(r'(?:proxmenux-update-[a-z0-9]+-[0-9a-f]{64}' @@ -82,10 +83,10 @@ def main(arguments): command = arguments[0] if arguments else 'prune' if command == 'prune': for path, size in prune(): - print(f'removed unused image archive: {path} ({size} bytes)') + print(translate('removed unused image archive: {path} ({size} bytes)').format(path=path, size=size)) return 0 if command not in ('list', 'remove') or not all(a.isdigit() for a in arguments[1:]): - print('usage: oci_image_cache.py [prune | list VMID... | remove VMID...]', file=sys.stderr) + print(translate('Usage:') + ' oci_image_cache.py [prune | list VMID... | remove VMID...]', file=sys.stderr) return 2 with instances.locked(instances.ROOT): archives = archives_of([int(a) for a in arguments[1:]]) @@ -103,5 +104,5 @@ if __name__ == '__main__': try: sys.exit(main(sys.argv[1:])) except (OSError, ValueError, BlockingIOError) as error: - print(f'image cache: {error}', file=sys.stderr) + print(f"{translate('Image cache')}: {error}", file=sys.stderr) sys.exit(1) diff --git a/oci/remote/oci_immich_recognition.py b/oci/remote/oci_immich_recognition.py index 721b79ff..6dc5c615 100644 --- a/oci/remote/oci_immich_recognition.py +++ b/oci/remote/oci_immich_recognition.py @@ -284,7 +284,7 @@ def change(root, primary_id, acceleration, render_device=None, gfx_override=None def main(): parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('vmid', type=int, help='main container of the application') + parser.add_argument('vmid', type=int, help=translate('main container of the application')) parser.add_argument('--acceleration', required=True, choices=PROFILES) parser.add_argument('--render-device') parser.add_argument('--gfx-override') diff --git a/oci/remote/oci_installation_state.py b/oci/remote/oci_installation_state.py index ecc10691..344d88ac 100644 --- a/oci/remote/oci_installation_state.py +++ b/oci/remote/oci_installation_state.py @@ -301,7 +301,7 @@ def main(argv=None): diagnose = sub.add_parser('diagnose') diagnose.add_argument('vmid', type=int) diagnose.add_argument('--check-registry', action='store_true') - record = sub.add_parser('record', help='Internal installer operation; not legacy adoption') + record = sub.add_parser('record', help=translate('Internal installer operation; not legacy adoption')) record.add_argument('vmid', type=int) for flag in ('template', 'deployment', 'archive', 'digest'): record.add_argument('--' + flag, required=True) diff --git a/oci/remote/oci_restore_recovery.py b/oci/remote/oci_restore_recovery.py index ab1827df..ebab98a9 100644 --- a/oci/remote/oci_restore_recovery.py +++ b/oci/remote/oci_restore_recovery.py @@ -1029,9 +1029,9 @@ def main(): parser.add_argument('--root', type=Path, default=instances.ROOT) parser.add_argument('--start', action='store_true') parser.add_argument('--host-firewall', type=int, action='append', default=[], metavar='VMID', - help='Add the host firewall rule of this container; one option for each confirmed rule') + help=translate('Add the host firewall rule of this container; one option for each confirmed rule')) parser.add_argument('--automatic', action='store_true', - help='Only the applications that need no question: the ones the cluster keeps a record of') + help=translate('Only the applications that need no question: the ones the cluster keeps a record of')) args = parser.parse_args() if os.geteuid() != 0: parser.error(translate('Root privileges are required')) diff --git a/oci/remote/oci_rocm_check.py b/oci/remote/oci_rocm_check.py index d4725d9d..711df27a 100644 --- a/oci/remote/oci_rocm_check.py +++ b/oci/remote/oci_rocm_check.py @@ -12,6 +12,7 @@ import argparse import re import subprocess import sys +from oci_ui import translate # A model of five operators (convolution, pooling and a dense layer), enough # to make ROCm compile and run on the GPU without downloading anything. @@ -72,7 +73,8 @@ def main(): try: check(args.vmid) except RuntimeError as error: - print(error, file=sys.stderr) + message = translate('the GPU did not answer in time') if str(error) == 'the GPU did not answer in time' else str(error) + print(message, file=sys.stderr) return 1 return 0 diff --git a/oci/remote/oci_update_check.py b/oci/remote/oci_update_check.py index 3d2faa9b..a1572431 100644 --- a/oci/remote/oci_update_check.py +++ b/oci/remote/oci_update_check.py @@ -18,6 +18,7 @@ import time import oci_instances as instances from oci_installation_state import parse_config +from oci_ui import translate APPS = Path('/etc/proxmenux/apps') SOCKET = Path('/run/proxmenux-monitor/oci-check.sock') @@ -206,7 +207,7 @@ def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--root', type=Path, default=instances.ROOT) parser.add_argument('--configs', type=Path, default=Path('/etc/pve/lxc')) - parser.add_argument('--progress', action='store_true', help='Emit numeric progress for the OCI menu') + parser.add_argument('--progress', action='store_true', help=translate('Emit numeric progress for the OCI menu')) args = parser.parse_args() try: if args.progress: diff --git a/oci/remote/oci_watchdog.py b/oci/remote/oci_watchdog.py index cb14fdf9..743d7fb1 100644 --- a/oci/remote/oci_watchdog.py +++ b/oci/remote/oci_watchdog.py @@ -19,6 +19,7 @@ import time import oci_instances as instances import oci_operation_notice +from oci_ui import translate UNIT = 'proxmenux-oci-watchdog.service' UNIT_FILE = Path('/etc/systemd/system') / UNIT @@ -191,7 +192,8 @@ def look(states, now): lambda seen: stop_requested(read_tasks(), vmid, seen)) data = {'app_name': name, 'vmid': vmid, 'containers': f'CT {vmid}'} if action == 'start': - print(f'CT {vmid} ({name}) stopped on its own; starting it again (attempt {state["attempts"]})', flush=True) + print(translate('CT {vmid} ({name}) stopped on its own; starting it again (attempt {attempt})').format( + vmid=vmid, name=name, attempt=state['attempts']), flush=True) try: started = start(vmid) except (OSError, subprocess.SubprocessError): @@ -199,7 +201,8 @@ def look(states, now): if started and notice_due(state, now): oci_operation_notice.notify('oci_watchdog_restarted', data) elif action == 'give-up': - print(f'CT {vmid} ({name}) could not be kept running; it is left stopped', flush=True) + print(translate('CT {vmid} ({name}) could not be kept running; it is left stopped').format( + vmid=vmid, name=name), flush=True) oci_operation_notice.notify('oci_watchdog_failed', data) @@ -290,7 +293,7 @@ def main(): try: vmids = set_watchdog(instances.ROOT, args.vmid, args.state == 'on') except BlockingIOError: - print('Another OCI operation is using the instance registry.', file=sys.stderr) + print(translate('Another OCI operation is using the instance registry.'), file=sys.stderr) return 3 except (OSError, ValueError, KeyError) as error: print(str(error) or type(error).__name__, file=sys.stderr) diff --git a/oci/remote/rclone_mount_publish.py b/oci/remote/rclone_mount_publish.py index df79c67f..950241f1 100644 --- a/oci/remote/rclone_mount_publish.py +++ b/oci/remote/rclone_mount_publish.py @@ -4,9 +4,32 @@ from __future__ import annotations import ctypes +import json import os import platform import sys +from pathlib import Path +from functools import lru_cache + + +@lru_cache(maxsize=1) +def _messages() -> dict: + # This helper is also installed alone in libexec: importing oci_ui would + # break that existing caller. Read the same catalog using stdlib only. + base = Path(os.environ.get('PMX_BASE_DIR', '/usr/local/share/proxmenux')) + try: + language = json.loads((base / 'config.json').read_text(encoding='utf-8')).get('language', 'en') + if language == 'en' or not isinstance(language, str) or not language: + return {} + data = json.loads((base / 'lang' / (language + '.json')).read_text(encoding='utf-8')) + return data if isinstance(data, dict) else {} + except (OSError, ValueError, AttributeError): + return {} + + +def translate(text: str) -> str: + value = _messages().get(text) + return value if isinstance(value, str) and value else text AT_FDCWD = -100 @@ -41,14 +64,16 @@ def fail(step: str) -> None: def main() -> int: if len(sys.argv) != 5 or sys.argv[4] not in {"rw", "ro"}: - print(f"usage: {sys.argv[0]} PID SOURCE TARGET rw|ro", file=sys.stderr) + print(f"{translate('Usage:')} {sys.argv[0]} PID SOURCE TARGET rw|ro", file=sys.stderr) return 2 machine = platform.machine().lower() if machine not in SYSCALLS: - print(f"unsupported host architecture: {machine}", file=sys.stderr) + print(f"{translate('Unsupported host architecture:')} {machine}", file=sys.stderr) return 2 open_tree_nr, move_mount_nr, mount_setattr_nr = SYSCALLS[machine] pid, source, target, mode = sys.argv[1:] + # The host catalog is no longer reachable after chroot into the container. + _messages() libc = ctypes.CDLL(None, use_errno=True) libc.syscall.restype = ctypes.c_long libc.setns.argtypes = (ctypes.c_int, ctypes.c_int) @@ -60,7 +85,7 @@ def main() -> int: ct_root = os.open(f"/proc/{pid}/root", os.O_PATH | os.O_DIRECTORY | os.O_CLOEXEC) try: if libc.setns(ct_ns, CLONE_NEWNS) != 0: - fail("enter container namespace") + fail(translate("enter container namespace")) os.fchdir(ct_root) os.chroot(".") os.chdir("/") @@ -71,7 +96,7 @@ def main() -> int: OPEN_TREE_CLONE | os.O_CLOEXEC, ) if tree < 0: - fail("clone source mount tree") + fail(translate("clone source mount tree")) try: if mode == "ro": attributes = MountAttr(attr_set=MOUNT_ATTR_RDONLY) @@ -84,9 +109,9 @@ def main() -> int: ctypes.sizeof(attributes), ) if result != 0: - fail("make cloned mount tree read-only") + fail(translate("make cloned mount tree read-only")) if libc.setns(host_ns, CLONE_NEWNS) != 0: - fail("return to host namespace") + fail(translate("return to host namespace")) os.fchdir(host_root) os.chroot(".") os.chdir("/") @@ -99,7 +124,7 @@ def main() -> int: MOVE_MOUNT_F_EMPTY_PATH, ) if result != 0: - fail("publish mount tree") + fail(translate("publish mount tree")) finally: os.close(tree) finally: diff --git a/oci/remote/verify_oci_archive.py b/oci/remote/verify_oci_archive.py index 29cedede..782a8dc0 100755 --- a/oci/remote/verify_oci_archive.py +++ b/oci/remote/verify_oci_archive.py @@ -164,7 +164,7 @@ def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("archive", type=Path) parser.add_argument("--extracted-mib", action="store_true", - help="print the estimated unpacked size in MiB (0 when unknown)") + help=translate("print the estimated unpacked size in MiB (0 when unknown)")) args = parser.parse_args() if args.extracted_mib: try: diff --git a/oci/src/proxmenux_oci/cli.py b/oci/src/proxmenux_oci/cli.py index 7199a4dd..d2d2c25f 100644 --- a/oci/src/proxmenux_oci/cli.py +++ b/oci/src/proxmenux_oci/cli.py @@ -472,7 +472,7 @@ def _rclone_mount(catalog: Catalog, ui) -> None: if result: from .management import carry_records carry_records(PROJECT_ROOT, shown=True) - console.msg_ok(f"Remote: {result['remote']}:") + console.msg_ok(f"{translate('Rclone remote')}: {result['remote']}:") console.msg_ok(f"{translate('Read/write')}: {result['read_write_path']}") console.msg_ok(f"{translate('Read-only')}: {result['read_only_path']}") console.wait_for_enter(translate("Press Enter to return to the menu...")) @@ -623,52 +623,52 @@ def _template_summary_text(template: dict[str, Any]) -> str: contract = template["container_contract"] lines = [ source_text(template["catalog_ui"]["title"]), - f"Image: {contract['image']['reference']}", - f"Status: {template['status']}", - "Ports: " + (", ".join(f"{p['container_port']}/{p['protocol']}" for p in contract["ports"]) or "none"), - "Volumes: " + (", ".join(v["container_path"] for v in contract["volumes"]) or "none"), + f"{translate('Image')}: {contract['image']['reference']}", + f"{translate('Status')}: {template['status']}", + translate("Ports") + ": " + (", ".join(f"{p['container_port']}/{p['protocol']}" for p in contract["ports"]) or translate("none")), + translate("Volumes") + ": " + (", ".join(v["container_path"] for v in contract["volumes"]) or translate("none")), ] blockers = template["compatibility"]["untranslated_blockers"] if blockers: - lines.append(f"Blockers: {', '.join(blockers)}") + lines.append(f"{translate('Blockers')}: {', '.join(blockers)}") return "\n".join(lines) def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(prog="oci_manager_apps.sh", - description="ProxMenux OCI manager Apps (Technology Preview)") + description=translate("ProxMenux OCI manager Apps (Technology Preview)")) subparsers = parser.add_subparsers(dest="command") - subparsers.add_parser("sync", help="Refresh the index from the image sources") - list_parser = subparsers.add_parser("list", help="List applications") + subparsers.add_parser("sync", help=translate("Refresh the index from the image sources")) + list_parser = subparsers.add_parser("list", help=translate("List applications")) list_parser.add_argument("--filter", default="") - generate_parser = subparsers.add_parser("generate", help="Regenerate the template of one application") + generate_parser = subparsers.add_parser("generate", help=translate("Regenerate the template of one application")) generate_parser.add_argument("app") - generate_all_parser = subparsers.add_parser("generate-all", help="Regenerate the catalog templates") + generate_all_parser = subparsers.add_parser("generate-all", help=translate("Regenerate the catalog templates")) generate_all_parser.add_argument("--provider", choices=["all", "linuxserver.io", "imported", "curated"], default="all") - subparsers.add_parser("apply-icons", help="Resolve catalog icons against the jsdelivr icon sets") - show_parser = subparsers.add_parser("show", help="Show the summary of a template") + subparsers.add_parser("apply-icons", help=translate("Resolve catalog icons against the jsdelivr icon sets")) + show_parser = subparsers.add_parser("show", help=translate("Show the summary of a template")) show_parser.add_argument("app") - open_parser = subparsers.add_parser("open", help="Open the page of an application in the menu") + open_parser = subparsers.add_parser("open", help=translate("Open the page of an application in the menu")) open_parser.add_argument("app") - install_parser = subparsers.add_parser("install", help="Configure and install an application") + install_parser = subparsers.add_parser("install", help=translate("Configure and install an application")) install_parser.add_argument("app") - install_parser.add_argument("--host", default="auto", help="'auto'/'local', or root@IP for development") + install_parser.add_argument("--host", default="auto", help=translate("'auto'/'local', or root@IP for development")) install_parser.add_argument("--advanced", action="store_true") install_parser.add_argument("--dry-run", action="store_true") - rclone_parser = subparsers.add_parser("rclone-mount", help="Enable a mount on an installed Rclone OCI") + rclone_parser = subparsers.add_parser("rclone-mount", help=translate("Enable a mount on an installed Rclone OCI")) rclone_parser.add_argument("--host", default="auto") rclone_parser.add_argument("--dry-run", action="store_true") - subparsers.add_parser("recover", help="Register again the OCI applications restored from a backup") - manage_parser = subparsers.add_parser("manage", help="Update, modify or recreate one installed OCI instance") + subparsers.add_parser("recover", help=translate("Register again the OCI applications restored from a backup")) + manage_parser = subparsers.add_parser("manage", help=translate("Update, modify or recreate one installed OCI instance")) manage_parser.add_argument("vmid", type=int) manage_parser.add_argument("--action", choices=("update", "modify", "recreate"), required=True) manage_parser.add_argument("--keep-backup", metavar="STORAGE", - help="Keep the backup taken before the update in this Proxmox storage") + help=translate("Keep the backup taken before the update in this Proxmox storage")) manage_parser.add_argument("--unattended", action="store_true", - help="Scheduled run: no questions; stops where a person has to decide") + help=translate("Scheduled run: no questions; stops where a person has to decide")) manage_parser.add_argument("--acknowledge-external-data", action="store_true", - help="Host directories are not reverted by the backup (confirmed beforehand)") + help=translate("Host directories are not reverted by the backup (confirmed beforehand)")) manage_parser.add_argument("--min-image-age-days", type=int, default=0) return parser @@ -681,15 +681,15 @@ def main(argv: list[str] | None = None) -> int: return interactive(catalog) if args.command == "sync": payload = catalog.sync_index() - print(f"Index updated: {len(payload['applications'])} candidate applications") + print(translate('Index updated: {count} candidate applications').format(count=len(payload['applications']))) return 0 if args.command == "list": query = args.filter.casefold() for item in catalog.load_index()["applications"]: if not item.get("hidden", False) and query in item["id"].casefold(): candidate = item.get("automatic_install_candidate") - status = ("installable" if candidate else "pending adaptation" if candidate is False - else item.get("template_status") or "not generated") + status = (translate("installable") if candidate else translate("pending adaptation") if candidate is False + else item.get("template_status") or translate("not generated")) print(f"{item['id']:<30} {_display_architectures(item):<12} {publisher(item):<14} {status}") return 0 if args.command == "generate": @@ -702,13 +702,12 @@ def main(argv: list[str] | None = None) -> int: print(f"\r[{current:3}/{total}] {marker:<5} {app_id:<32}", end="", flush=True) report = catalog.generate_all(progress=progress, provider=args.provider) - print(f"\nGenerated: {report['generated_count']}; failed: {report['failed_count']}; " - f"family: {args.provider}") + print('\n' + translate('Generated: {generated}; failed: {failed}; family: {provider}').format( + generated=report['generated_count'], failed=report['failed_count'], provider=args.provider)) return 0 if not report["failed"] else 2 if args.command == "apply-icons": report = catalog.apply_icons() - print(f"Icons resolved: {report['resolved']}; cleared: {report['cleared']}; " - f"unchanged: {report['unchanged']}; with a theme variant: {report['themed']}") + print(translate('Icons resolved: {resolved}; cleared: {cleared}; unchanged: {unchanged}; with a theme variant: {themed}').format(**report)) return 0 if args.command == "show": print(_template_summary_text(catalog.compose(args.app))) @@ -728,14 +727,14 @@ def main(argv: list[str] | None = None) -> int: if args.command == "install": template = catalog.compose(args.app) if not template["compatibility"]["automatic_install_candidate"]: - raise InstallError("Installation blocked: " + ", ".join(template["compatibility"]["untranslated_blockers"])) + raise InstallError(translate("Installation blocked:") + " " + ", ".join(template["compatibility"]["untranslated_blockers"])) notice = external_services_notice(template) if notice: print(notice + "\n") deployment = build_deployment(template, None, ADVANCED_MODE if args.advanced else DEFAULT_MODE) print(_deployment_summary_text(template, deployment)) - if not args.dry_run and input("\nInstall? [y/N]: ").strip().casefold() not in {"y", "yes"}: - print("Installation cancelled.") + if not args.dry_run and input('\n' + translate('Install? [y/N]:') + ' ').strip().casefold() not in {"y", "yes"}: + print(translate("Installation cancelled.")) return 0 result = run_remote_install(PROJECT_ROOT, template, deployment, args.host, args.dry_run) if result: @@ -764,7 +763,7 @@ def main(argv: list[str] | None = None) -> int: print(json.dumps(deployment, ensure_ascii=False, indent=2)) result = run_remote_rclone_mount(PROJECT_ROOT, template, deployment, args.host, args.dry_run) if result: - print(f"Read/write: {result['read_write_path']}\nRead-only: {result['read_only_path']}") + print(f"{translate('Read/write')}: {result['read_write_path']}\n{translate('Read-only')}: {result['read_only_path']}") return 0 return 1 except (KeyboardInterrupt, UserCancelled): diff --git a/oci/src/proxmenux_oci/i18n.py b/oci/src/proxmenux_oci/i18n.py index 4efa371a..737ffb69 100644 --- a/oci/src/proxmenux_oci/i18n.py +++ b/oci/src/proxmenux_oci/i18n.py @@ -57,10 +57,55 @@ def diagnostic(error: Any) -> str: for pattern, source in ( (r"No template is available for '(.*)'", N_("No template is available for '{app_id}'")), (r"The application '(.*)' is not in the index", N_("The application '{app_id}' is not in the index")), + (r"The application '(.*)' does not come from LinuxServer", N_("The application '{app_id}' does not come from LinuxServer")), ): match = re.fullmatch(pattern, text) if match: return translate(source).format(app_id=match[1]) + exact = { + 'The CasaOS Compose file has no services': N_('The Compose file has no services.'), + 'The Compose file has no services': N_('The Compose file has no services.'), + 'x-casaos.main no identifica un servicio valido': N_('x-casaos.main must name an existing service.'), + 'The main CasaOS service declares no image': N_('The main Compose service has no image.'), + 'El servicio principal no declara una imagen': N_('The main Compose service has no image.'), + 'El servicio no declara una imagen': N_('The service declares no image:'), + 'The CasaOS document is not a Compose mapping': N_('The Compose document must be a mapping.'), + 'The Compose file has no x-casaos metadata': N_('The Compose file has no x-casaos metadata.'), + 'environment must be a list or a mapping': N_('Compose environment must be a list or a mapping.'), + 'volumes must be a list': N_('Compose volumes must be a list.'), + 'ports must be a list': N_('Compose ports must be a list.'), + 'The README has no recognisable docker-compose section': N_('The README has no recognisable docker-compose section'), + 'La seccion docker-compose no contiene un bloque de codigo': N_('The docker-compose section contains no code block'), + 'The docker-compose block is not closed': N_('The docker-compose block is not closed'), + 'The block found does not look like a Docker Compose file': N_('The block found does not look like a Docker Compose file'), + 'No se puede identificar la imagen principal del Compose': N_('The main Compose service cannot be identified unambiguously'), + 'No se puede identificar de forma univoca el servicio principal del Compose': N_('The main Compose service cannot be identified unambiguously'), + 'python3-jsonschema is required to generate templates': N_('python3-jsonschema is required to generate templates'), + 'Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application': N_('Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application'), + 'The ProxMenux application catalogue is not a list': N_('The ProxMenux application catalogue is not a list'), + 'GitHub returned a truncated tree for the CasaOS catalogue': N_('GitHub returned a truncated tree for the CasaOS catalogue'), + 'No Compose files were found in the CasaOS catalogue': N_('No Compose files were found in the CasaOS catalogue'), + } + if text in exact: + return translate(exact[text]) + for prefix, source in ( + ('Cannot normalise the CasaOS identifier:', N_('Invalid application identifier:')), + ('Cannot normalise the identifier:', N_('Invalid application identifier:')), + ('Docker Compose CasaOS no valido:', N_('The Compose file is not valid YAML:')), + ('Docker Compose no valido:', N_('The Compose file is not valid YAML:')), + ('La plantilla generada no cumple el esquema:', N_('The generated template does not match the schema:')), + ): + if text.startswith(prefix): + return translate(source) + text[len(prefix):] + match = re.fullmatch(r'GitHub returned HTTP 403 for (.+)\. Set GITHUB_TOKEN to raise the API rate limit\.', text) + if match: + return translate('GitHub returned HTTP 403 for {url}. Set GITHUB_TOKEN to raise the API rate limit.').format(url=match[1]) + match = re.fullmatch(r'GitHub returned HTTP ([0-9]{3}) (?:for|para) (.+)\.', text) + if match: + return translate('GitHub returned HTTP {status} for {url}.').format(status=match[1], url=match[2]) + match = re.fullmatch(r'No se pudo acceder a (.+) despues de 3 intentos: (.*)', text, re.S) + if match: + return translate('Could not reach {url} after 3 attempts: {reason}').format(url=match[1], reason=match[2]) return text From 153743b80d5df3b66260184101d6cabe7243c2a1 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:25:14 +0000 Subject: [PATCH 23/27] i18n: clarify OCI network bridge labels in Slovak --- lang/sk.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lang/sk.json b/lang/sk.json index 55ceb340..8afac266 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -255,11 +255,11 @@ "Accept routes from other nodes?": "Prijímať trasy z iných uzlov?", "Accept this host monitoring profile?": "Prijať tento profil monitorovania hostiteľa?", "Access Scope:": "Rozsah prístupu:", - "Access bridge": "Prístupový mostík", - "Access bridge for Immich": "Prístupový mostík pre Immich", - "Access bridge for Nextcloud": "Prístupový mostík pre Nextcloud", - "Access bridge for Paperless": "Prístupový most pre Paperless", - "Access bridge for Tandoor": "Prístupový mostík pre Tandoor", + "Access bridge": "Sieťový most", + "Access bridge for Immich": "Sieťový most pre Immich", + "Access bridge for Nextcloud": "Sieťový most pre Nextcloud", + "Access bridge for Paperless": "Sieťový most pre Paperless", + "Access bridge for Tandoor": "Sieťový most pre Tandoor", "Access could not be granted completely; the application may be unable to write to:": "Prístup nebolo možné úplne udeliť; aplikácia možno nebude môcť zapisovať do:", "Access could not be granted; the application cannot write to:": "Prístup nebolo možné udeliť; aplikácia nemôže písať na:", "Access denied by {registry}. Check the image name and sign in from Registry accounts.": "Register {registry} odmietol prístup. Skontrolujte názov obrazu a prihláste sa cez Účty registrov.", From bb8f5b5c064c4355078cf43d422dec7cd796c4fb Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:30:48 +0000 Subject: [PATCH 24/27] i18n: retain bridge term alongside Slovak network labels --- lang/sk.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lang/sk.json b/lang/sk.json index 8afac266..4668be8a 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -255,11 +255,11 @@ "Accept routes from other nodes?": "Prijímať trasy z iných uzlov?", "Accept this host monitoring profile?": "Prijať tento profil monitorovania hostiteľa?", "Access Scope:": "Rozsah prístupu:", - "Access bridge": "Sieťový most", - "Access bridge for Immich": "Sieťový most pre Immich", - "Access bridge for Nextcloud": "Sieťový most pre Nextcloud", - "Access bridge for Paperless": "Sieťový most pre Paperless", - "Access bridge for Tandoor": "Sieťový most pre Tandoor", + "Access bridge": "Sieťový most (bridge)", + "Access bridge for Immich": "Sieťový most (bridge) pre Immich", + "Access bridge for Nextcloud": "Sieťový most (bridge) pre Nextcloud", + "Access bridge for Paperless": "Sieťový most (bridge) pre Paperless", + "Access bridge for Tandoor": "Sieťový most (bridge) pre Tandoor", "Access could not be granted completely; the application may be unable to write to:": "Prístup nebolo možné úplne udeliť; aplikácia možno nebude môcť zapisovať do:", "Access could not be granted; the application cannot write to:": "Prístup nebolo možné udeliť; aplikácia nemôže písať na:", "Access denied by {registry}. Check the image name and sign in from Registry accounts.": "Register {registry} odmietol prístup. Skontrolujte názov obrazu a prihláste sa cez Účty registrov.", From c00207bd202ca559d5ce78ec30e497a866181762 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:33:57 +0000 Subject: [PATCH 25/27] i18n: unify Slovak network bridge wording across the app --- AppImage/messages/sk/common.json | 28 ++++++++-------- lang/sk.json | 56 ++++++++++++++++---------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json index b9d2498d..3665399a 100644 --- a/AppImage/messages/sk/common.json +++ b/AppImage/messages/sk/common.json @@ -1032,7 +1032,7 @@ "pciPassthrough": "PCI Passthrough", "usbDevices": "USB zariadenia", "serialPorts": "Sériové porty", - "bridge": "Sieťový bridge", + "bridge": "Sieťový most (bridge)", "enabled": "Zapnuté", "disabled": "Vypnuté", "on": "zapnuté", @@ -1874,7 +1874,7 @@ }, "networkInterfaces": { "title": "Vylúčené sieťové rozhrania", - "description": "Vylúčte sieťové rozhrania, napríklad most, bond alebo fyzické sieťové karty, z kontroly stavu a upozornení. Hodí sa to pre rozhrania, ktoré sú zámerne vypnuté alebo sa nepoužívajú.", + "description": "Vylúčte sieťové rozhrania, napríklad sieťové mosty (bridge), bond alebo fyzické sieťové karty, z kontroly stavu a upozornení. Hodí sa to pre rozhrania, ktoré sú zámerne vypnuté alebo sa nepoužívajú.", "emptyTitle": "Nenašli sa žiadne sieťové rozhrania", "interface": "Rozhranie", "down": "VYPNUTÉ", @@ -3506,7 +3506,7 @@ }, "loading": { "title": "Načítavam údaje o sieti...", - "description": "Kontrolujem rozhrania, sieťové mosty a premávku" + "description": "Kontrolujem rozhrania, sieťové mosty (bridge) a premávku" }, "errors": { "serverUnavailableTitle": "Server nie je dostupný", @@ -3520,7 +3520,7 @@ }, "sections": { "physicalInterfaces": "Fyzické rozhrania", - "bridgeInterfaces": "Sieťové mosty", + "bridgeInterfaces": "Sieťové mosty (bridge)", "vmLxcInterfaces": "Sieťové rozhrania VM a LXC" }, "summary": { @@ -3540,8 +3540,8 @@ }, "interfaceTypes": { "physical": "Fyzické", - "bridge": "Most", - "bridges": "Mosty", + "bridge": "Sieťový most (bridge)", + "bridges": "Sieťové mosty (bridge)", "bond": "Bond", "vlan": "VLAN", "virtual": "Virtuálne", @@ -3635,7 +3635,7 @@ "inactiveTitle": "Rozhranie je neaktívne", "inactiveDescription": "Toto rozhranie je momentálne vypnuté. Štatistiky sieťovej premávky nie sú dostupné.", "bondConfiguration": "Nastavenie bondu", - "bridgeConfiguration": "Nastavenie mosta" + "bridgeConfiguration": "Nastavenie sieťového mosta (bridge)" }, "flow": { "title": "Tok siete" @@ -6054,11 +6054,11 @@ } }, "bridge_without_ports": { - "title": "Porty bridgeov", - "rationale": "Konfigurácia portov každého bridge. Bridge bez fyzického portu obsluhuje internú alebo smerovanú sieť.", + "title": "Porty sieťových mostov (bridge)", + "rationale": "Konfigurácia portov každého sieťového mosta (bridge). Sieťový most (bridge) bez fyzického portu obsluhuje internú alebo smerovanú sieť.", "summary": { - "allConnected": "Všetkých {total} bridgeov má port", - "isolated": "{count} z {total} bridgeov nemá port", + "allConnected": "Všetkých {total} sieťových mostov (bridge) má port", + "isolated": "{count} z {total} sieťových mostov (bridge) nemá port", "evaluationFailed": "Kontrolu sa nepodarilo vyhodnotiť" } } @@ -6274,13 +6274,13 @@ "network": "Sieť", "adapters": "Adaptéry", "bond": "Bond", - "bridge": "Bridge", - "bridges": "Bridge", + "bridge": "Sieťový most (bridge)", + "bridges": "Sieťové mosty (bridge)", "physicalAdapters": "Fyzické adaptéry", "interface": "Rozhranie", "driver": "Ovládač", "state": "Stav", - "networkDiagramNote": "Cesta od kábla ku každému VM a LXC: fyzický adaptér, bond, ak ich zoskupuje, bridge a pripojené VM a LXC.", + "networkDiagramNote": "Cesta od kábla ku každému VM a LXC: fyzický adaptér, bond, ak ich zoskupuje, sieťový most (bridge) a pripojené VM a LXC.", "storageAndProtection": "Úložisko a ochrana", "storage": "Úložisko", "content": "Obsah", diff --git a/lang/sk.json b/lang/sk.json index 4668be8a..1f9d0d85 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -466,9 +466,9 @@ "An update does not accept configuration changes": "Aktualizácia neprijíma zmeny konfigurácie", "Analysis Tools": "Analytické nástroje", "Analysis software that shows your internet speed for up to 30 days.": "Analyzačný softvér, ktorý ukazuje rýchlosť internetu až 30 dní.", - "Analyze Bridge Configuration": "Analyzovať nastavenie bridge", + "Analyze Bridge Configuration": "Analyzovať nastavenie sieťového mosta (bridge)", "Analyze Network Configuration": "Analyzovať nastavenie siete", - "Analyzing Bridge Configuration - READ ONLY MODE": "Analyzujem nastavenie bridge - režim iba na čítanie", + "Analyzing Bridge Configuration - READ ONLY MODE": "Analyzujem nastavenie sieťového mosta (bridge) - režim iba na čítanie", "Analyzing Network Configuration - READ ONLY MODE": "Analyzujem nastavenie siete - režim iba na čítanie", "Analyzing selected disks...": "Analyzujem vybrané disky...", "Analyzing system for available PCIe storage devices...": "Analyzujem systém a hľadám dostupné PCIe úložiskové zariadenia...", @@ -632,7 +632,7 @@ "BIOS from": "BIOS z", "BIOS: OVMF (UEFI)": "BIOS: OVMF (UEFI)", "BOINC is a platform for high-throughput computing on a large scale (thousands or millions of computers). It can be used for volunteer computing (using consumer devices) or grid computing (using organizational resources). It supports virtualized, parallel, and GPU-based applications.": "BOINC je platforma pre vysokovýkonnú výpočtovú techniku vo veľkom meradle (v tisícoch alebo miliónoch počítačov). Môže byť použitý pre dobrovoľnú výpočtovú techniku (pomocou spotrebiteľských zariadení) alebo grid computing (pomocou organizačných zdrojov). Podporuje virtualizované, paralelné a GPU-založené aplikácie.", - "BRIDGE CONFIGURATION ANALYSIS": "ANALÝZA NASTAVENIA BRIDGE", + "BRIDGE CONFIGURATION ANALYSIS": "ANALÝZA NASTAVENIA SIEŤOVÉHO MOSTA (bridge)", "BTRFS:": "BTRFS:", "Baby Buddy web interface": "Webové rozhranie Baby Buddy", "Babybuddy is a buddy for babies! Helps caregivers track sleep, feedings, diaper changes, tummy time and more to learn about and predict baby's needs without (as much) guess work.": "Baby Buddy pomáha pri starostlivosti o dieťa sledovaním spánku, kŕmenia, výmen plienok, času na brušku a ďalších činností. Uľahčuje porozumenie potrebám dieťaťa a ich predvídanie.", @@ -745,11 +745,11 @@ "Borg repository path:": "Cesta Borg repozitára:", "Borg restore error log": "Záznam chyby obnovy z Borg", "Borg server SSH user": "SSH používateľ Borg servera", - "Bridge": "Bridge", - "Bridge Analysis": "Analýza bridge rozhraní", - "Bridge Configuration Analysis": "Analýza nastavenia bridge", - "Bridge:": "Bridge:", - "Bridges analyzed": "Skontrolované bridge rozhrania", + "Bridge": "Sieťový most (bridge)", + "Bridge Analysis": "Analýza sieťových mostov (bridge)", + "Bridge Configuration Analysis": "Analýza nastavenia sieťového mosta (bridge)", + "Bridge:": "Sieťový most (bridge):", + "Bridges analyzed": "Skontrolované sieťové mosty (bridge)", "Broken gasket-dkms package state recovered.": "Poškodený stav balíka gasket-dkms bol opravený.", "Browse Your Life in Images": "Prehliadajte svoj život v obrazoch", "Browse manually (advanced)...": "Vybrať ručne (pokročilé)...", @@ -969,7 +969,7 @@ "Choose an action:": "Vyberte akciu:", "Choose an export to mount:": "Vyberte export, ktorý chcete pripojiť:", "Choose an option:": "Vyberte možnosť:", - "Choose another bridge for the container in Proxmox (Network) and run the recovery again.": "V Proxmoxe vyberte pre kontajner iný sieťový most (Sieť) a registráciu zopakujte.", + "Choose another bridge for the container in Proxmox (Network) and run the recovery again.": "V Proxmoxe vyberte pre kontajner iný sieťový most (bridge) v časti Sieť a registráciu zopakujte.", "Choose any host path you want to share with CTs.": "Vyberte ľubovoľnú cestu na serveri, ktorú chcete zdieľať s CT.", "Choose conflict policy": "Vyberte, ako vyriešiť konflikt", "Choose conflict policy for the source VM:": "Vyberte pravidlo riešenia konfliktu pre zdrojovú VM:", @@ -3333,7 +3333,7 @@ "Invalid administrator email": "Neplatný administrátorský mail", "Invalid administrator user name": "Neplatné meno správcu", "Invalid base VMID": "Neplatné základné VMID", - "Invalid bridge": "Neplatný sieťový most", + "Invalid bridge": "Neplatný sieťový most (bridge)", "Invalid check package:": "Neplatný kontrolný balík:", "Invalid configuration path:": "Neplatná konfiguračná dráha:", "Invalid consume/export volumes": "Neplatné dátové zväzky consume a export", @@ -3388,7 +3388,7 @@ "Invalid path": "Neplatná cesta", "Invalid path in the NVIDIA inventory": "Neplatná cesta v inventári NVIDIA", "Invalid post-start timeout in the configuration:": "Neplatný čas po štarte v konfigurácii:", - "Invalid private bridge": "Neplatný súkromný most", + "Invalid private bridge": "Neplatný súkromný sieťový most (bridge)", "Invalid private network": "Neplatná súkromná sieť", "Invalid prlimit value": "Neplatná hodnota prlimitu", "Invalid process limits format": "Neplatný formát obmedzení procesu", @@ -4067,7 +4067,7 @@ "Network :": "Sieť :", "Network & Firewall": "Sieť a firewall", "Network (interfaces, DNS)": "Sieť (rozhrania, DNS)", - "Network Bridge": "Sieťový bridge", + "Network Bridge": "Sieťový most (bridge)", "Network Commands": "Sieťové príkazy", "Network Configuration": "Nastavenie siete", "Network Configuration Analysis": "Analýza nastavenia siete", @@ -4084,7 +4084,7 @@ "Network Tools": "Sieťové nástroje", "Network UPS Tools server for a UPS connected to the host by USB.": "Server Network UPS Tools pre UPS pripojený k hostiteľovi cez USB.", "Network access:": "Sieťový prístup:", - "Network bridge": "Sieťový most", + "Network bridge": "Sieťový most (bridge)", "Network configuration backed up": "Nastavenie siete bolo zálohované", "Network configuration has been restored from backup.": "Nastavenie siete bolo obnovené zo zálohy.", "Network connection failed to": "Sieťové pripojenie zlyhalo k", @@ -4261,8 +4261,8 @@ "No backup is scheduled: the rsnapshot lines in /config/crontabs/root are commented out. Uncomment or adjust the intervals you want, then restart the container.": "Žiadna záloha nie je naplánovaná: riadky rsnapshot v /config/crontabs/root sú zakomentované. Odkomentujte alebo upravte požadované intervaly a potom kontajner reštartujte.", "No backups": "Žiadne zálohy", "No backups found": "Nenašli sa žiadne zálohy", - "No bridge configuration issues found": "V nastavení bridge sa nenašli problémy", - "No bridges found in system": "V systéme sa nenašli žiadne bridge rozhrania", + "No bridge configuration issues found": "V nastavení sieťového mosta (bridge) sa nenašli problémy", + "No bridges found in system": "V systéme sa nenašli žiadne sieťové mosty (bridge)", "No changes are required for": "Nie sú potrebné žiadne zmeny pre", "No changes are required.": "Nie sú potrebné žiadne zmeny.", "No changes have been made to your system": "V systéme neboli vykonané žiadne zmeny", @@ -5709,7 +5709,7 @@ "Select backup profile:": "Vyberte profil zálohy:", "Select backup to restore": "Vyberte zálohu na obnovu", "Select backup to restore:": "Vyberte zálohu na obnovu:", - "Select bridge for network interface(s):": "Vyberte bridge pre sieťové rozhrania:", + "Select bridge for network interface(s):": "Vyberte sieťový most (bridge) pre sieťové rozhrania:", "Select compression:": "Vyberte kompresiu:", "Select container:": "Vyberte kontajner:", "Select content types for this storage:": "Vyberte typy obsahu pre toto úložisko:", @@ -5800,7 +5800,7 @@ "Selected LXC action": "Vybraná LXC akcia", "Selected VM": "Vybraná VM", "Selected VM action": "Vybraná VM akcia", - "Selected bridge:": "Vybraný bridge:", + "Selected bridge:": "Vybraný sieťový most (bridge):", "Selected container": "Vybraný kontajner", "Selected controller/NVMe is already assigned to other VM(s):": "Vybraný Controller/NVMe už je priradený k iným VM:", "Selected device is already assigned to other VM(s):": "Vybrané zariadenie už je priradené k iným VM:", @@ -5858,10 +5858,10 @@ "Set VLAN (leave empty for none)": "Zadajte VLAN (nechajte prázdne, ak sa nemá použiť)", "Set VM boot order": "Nastaviť poradie bootovania VM", "Set Virtual Machine ID": "Zadať ID virtuálnej mašiny", - "Set a Bridge": "Zadať bridge", + "Set a Bridge": "Nastaviť sieťový most (bridge)", "Set a MAC Address": "Zadať MAC adresu", "Set a Vlan(leave blank for default)": "Zadať VLAN (nechajte prázdne pre predvolené)", - "Set network bridge (default: vmbr0)": "Zadajte sieťový bridge (predvolené: vmbr0)", + "Set network bridge (default: vmbr0)": "Zadajte sieťový most (bridge) (predvolené: vmbr0)", "Set ownership and permissions:": "Nastaviť vlastníka a oprávnenia:", "Set this disk as the primary boot disk?": "Nastaviť tento disk ako hlavný bootovací disk?", "Set up custom MOTD banner": "Nastaviť vlastnú MOTD správu", @@ -5946,7 +5946,7 @@ "Show active firewall rules (iptables)": "Zobraziť aktívne pravidlá firewallu (iptables)", "Show all active mounts from the kernel": "Zobraziť všetky aktívne pripojenia z kernelu", "Show available Proxmox upgrades": "Zobraziť dostupné aktualizácie Proxmoxu", - "Show configured network bridges": "Zobraziť nastavené sieťové bridge", + "Show configured network bridges": "Zobraziť nastavené sieťové mosty (bridge)", "Show container configuration. Use the correct ": "Zobraziť nastavenie kontajnera. Použite správne ", "Show detailed keyfile info": "Zobraziť podrobné informácie o keyfile", "Show disk usage by mount point": "Zobraziť využitie diskov podľa bodu pripojenia", @@ -6562,7 +6562,7 @@ "The host directory changed before it was mounted": "Adresár hostiteľa sa zmenil predtým, ako bol pripojený", "The host directory does not exist": "Adresár na hostiteľovi neexistuje", "The host directory may not be accessible from an unprivileged container.": "Priečinok na hostovi nemusí byť dostupný z neprivilegovaného kontajnera.", - "The host has no IPv4 address on the selected bridge": "Hostiteľ nemá IPv4 adresu na vybranom moste", + "The host has no IPv4 address on the selected bridge": "Hostiteľ nemá IPv4 adresu na vybranom sieťovom moste (bridge)", "The host loses access to them. If the group includes a network or storage controller the host needs, cancel at the summary.": "Hostiteľ k nim stratí prístup. Ak skupina obsahuje sieťový alebo úložný radič, ktorý hostiteľ potrebuje, zrušte to v súhrne.", "The host monitor does not see the real host memory": "Monitor hostiteľa nevidí skutočnú pamäť hostiteľa", "The host monitor does not share this host namespace:": "Monitor hostiteľa nezdieľa tento menný priestor hostiteľa:", @@ -6571,7 +6571,7 @@ "The host monitor profile does not support another sysctl include": "Profil monitora hostiteľa nepodporuje ďalší pripojený konfiguračný súbor sysctl", "The host monitor uses the host network, without DHCP or its own gateway": "Hostiteľský monitor využíva hostiteľskú sieť bez DHCP alebo vlastnej brány", "The host port is already in use:": "Hostiteľský port sa už používa:", - "The host-monitor firewall bridge does not match the selected bridge": "Bridge firewallu pre monitor hostiteľa nezodpovedá vybranému bridgeu", + "The host-monitor firewall bridge does not match the selected bridge": "Sieťový most (bridge) firewallu monitora hostiteľa nezodpovedá vybranému sieťovému mostu (bridge)", "The host-monitor firewall declaration is invalid": "Deklarácia firewallu pre monitor hostiteľa je neplatná", "The host-monitor firewall port does not match exactly one TCP port in the container contract": "Port firewallu monitora hostiteľa sa nezhoduje s práve jedným portom TCP v definícii kontajnera", "The host-monitor firewall port is invalid": "Port firewallu pre monitor hostiteľa je neplatný", @@ -6706,7 +6706,7 @@ "The previous stack contract is not valid; it is not archived automatically": "Predchádzajúca zmluva nie je platná; nie je automaticky archivovaná", "The previous stack contract still has containers or VMs:": "K predchádzajúcej definícii zostavy stále patria kontajnery alebo VM:", "The private address is already assigned to another container:": "Súkromná adresa je už pridelená k inému kontajneru:", - "The private bridge does not have the expected address:": "Súkromný most nemá očakávanú adresu:", + "The private bridge does not have the expected address:": "Súkromný sieťový most (bridge) nemá očakávanú adresu:", "The private journal has an unsafe owner or permissions": "Súkromný záznam operácie má nebezpečného vlastníka alebo prístupové práva", "The private network allocator was not found": "Nástroj na prideľovanie súkromných sietí sa nenašiel", "The private network is still used by another container and is kept:": "Súkromná sieť sa stále používa v inej nádobe a udržiava sa:", @@ -6799,8 +6799,8 @@ "The selected VM": "Vybraná VM", "The selected VM is running.": "Vybraná VM beží.", "The selected base folder does not exist and could not be created:": "Vybraný základný priečinok neexistuje a nepodarilo sa ho vytvoriť:", - "The selected bridge does not exist": "Vybraný sieťový most neexistuje", - "The selected bridge has no IPv4 subnet for the host-monitor firewall": "Vybraný bridge nemá IPv4 podsieť pre firewall monitora hostiteľa", + "The selected bridge does not exist": "Vybraný sieťový most (bridge) neexistuje", + "The selected bridge has no IPv4 subnet for the host-monitor firewall": "Vybraný sieťový most (bridge) nemá IPv4 podsieť pre firewall monitora hostiteľa", "The selected configuration needs to start the LXC during the installation": "Vybraná konfigurácia musí spustiť LXC počas inštalácie", "The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Vybraný kontajner je neprivilegovaný. Na priame priradenie zariadenia je potrebný privilegovaný kontajner.", "The selected device": "Vybrané zariadenie", @@ -7481,7 +7481,7 @@ "Verify Options > OS Type — currently set to:": "Overte Options > OS Type - aktuálne nastavené na:", "Verify PVE version (must be 8.4.1 or newer):": "Overte verziu PVE (musí byť 8.4.1 alebo novšia):", "Verify active exports:": "Overiť aktívne exporty:", - "Verify bridge assignment and NIC model — change to VirtIO if guest drivers are available": "Overte priradenie bridgu a model sieťovej karty - ak sú v hosťovi dostupné ovládače, zmeňte na VirtIO", + "Verify bridge assignment and NIC model — change to VirtIO if guest drivers are available": "Overte priradenie sieťového mosta (bridge) a model sieťovej karty - ak sú v hosťovi dostupné ovládače, zmeňte na VirtIO", "Verify iSCSI sessions and storage status:": "Overiť iSCSI relácie a stav úložiska:", "Verify inside container:": "Overiť vo vnútri kontajnera:", "Verify installations": "Overiť inštalácie", @@ -8190,8 +8190,8 @@ "systemd OnCalendar expression": "systemd OnCalendar výraz", "the API key was not generated on the first start": "kľúč API nebol vytvorený pri prvom štarte", "the NVIDIA hook of the container is not available:": "spúšťací skript NVIDIA pre tento kontajner nie je dostupný:", - "the bridge does not exist on this host:": "most na tomto hostiteľovi neexistuje:", - "the bridge exists on this host with another network:": "most existuje na tomto hostiteľovi s inou sieťou:", + "the bridge does not exist on this host:": "sieťový most (bridge) na tomto hostiteľovi neexistuje:", + "the bridge exists on this host with another network:": "sieťový most (bridge) existuje na tomto hostiteľovi s inou sieťou:", "the container has its own address, so the port Docker published on the host is not needed": "kontajner má vlastnú adresu, takže port zverejnený Dockerom na hostiteľovi nie je potrebný", "the containers do not agree on the private network of": "kontajnery sa nezhodujú na súkromnej sieti", "the device does not exist on this host:": "zariadenie na tomto hostiteľovi neexistuje:", From a191096ee4fa5306380c78dfcf69a593fa87cd5c Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Wed, 21 Oct 2026 07:11:28 +0000 Subject: [PATCH 26/27] test: allow English fallback for untranslated CLI catalogs --- .../scripts/tests/test_terminal_catalogs.py | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/.github/scripts/tests/test_terminal_catalogs.py b/.github/scripts/tests/test_terminal_catalogs.py index f74966cb..a69aedd8 100644 --- a/.github/scripts/tests/test_terminal_catalogs.py +++ b/.github/scripts/tests/test_terminal_catalogs.py @@ -69,13 +69,43 @@ class TerminalCatalogsTests(unittest.TestCase): def test_shipped_active_catalogs_keep_literal_contracts(self): result = subprocess.run( [sys.executable, '-B', str(SCRIPTS / 'check_terminal_catalogs.py'), - '--root', str(ROOT), '--require-complete'], + '--root', str(ROOT)], capture_output=True, text=True, timeout=60, ) self.assertEqual(result.returncode, 0, result.stdout + result.stderr) for locale in MODULE.DEFAULT_LANGUAGES: self.assertIn(f'[{locale}] active=', result.stdout) - self.assertNotIn('missing=1', result.stdout) + # New hand-reviewed Slovak keys ship with their source changes; + # other locales may use the runtime English fallback until translated. + result = subprocess.run( + [sys.executable, '-B', str(SCRIPTS / 'check_terminal_catalogs.py'), + '--root', str(ROOT), '--languages', 'sk', '--require-complete'], + capture_output=True, text=True, timeout=60, + ) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + self.assertIn('missing=0', result.stdout) + + def test_optional_completeness_still_rejects_invalid_present_translations(self): + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + (root / 'scripts').mkdir() + (root / 'lang').mkdir() + (root / 'scripts/check.sh').write_text('translate "Check /etc/pve/storage.cfg"') + (root / 'lang/sk.json').write_text(json.dumps({'Check /etc/pve/storage.cfg': 'Skontrolujte /etc/pve/storage.cfg'})) + (root / 'lang/es.json').write_text('{}') + + def check(languages, *flags): + return subprocess.run( + [sys.executable, '-B', str(SCRIPTS / 'check_terminal_catalogs.py'), + '--root', str(root), '--languages', languages, *flags], + capture_output=True, text=True, timeout=60, + ) + + self.assertEqual(check('es,sk').returncode, 0) + self.assertEqual(check('sk', '--require-complete').returncode, 0) + self.assertNotEqual(check('es', '--require-complete').returncode, 0) + (root / 'lang/es.json').write_text(json.dumps({'Check /etc/pve/storage.cfg': 'Compruebe /etc/PVE/storage.cfg'})) + self.assertNotEqual(check('es,sk').returncode, 0) if __name__ == '__main__': From ab1b6ce7a1d845aa1f0fe07d0c01264e00f4c442 Mon Sep 17 00:00:00 2001 From: VAIO73 <50487331+Vaso73@users.noreply.github.com> Date: Wed, 21 Oct 2026 07:39:23 +0000 Subject: [PATCH 27/27] test: install JSON Schema validator for offline OCI regressions --- .../scripts/tests/test_oci_complete_errors_sk.py | 16 ++++++++++++++++ .github/workflows/test-i18n.yml | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/scripts/tests/test_oci_complete_errors_sk.py b/.github/scripts/tests/test_oci_complete_errors_sk.py index 8b54d976..0a6bf0d4 100644 --- a/.github/scripts/tests/test_oci_complete_errors_sk.py +++ b/.github/scripts/tests/test_oci_complete_errors_sk.py @@ -50,6 +50,22 @@ class TechnicalMessages(unittest.TestCase): self.assertTrue(raw.startswith('La plantilla generada no cumple el esquema:')) self.assertEqual(i18n.diagnostic(caught.exception), 'Vygenerovaná šablóna nezodpovedá schéme:' + raw.split(':', 1)[1]) + def test_missing_schema_validator_is_distinct_from_invalid_schema_without_effects(self): + template = {} + read = self.patch(Path, 'read_text', side_effect=AssertionError('schema must not be read without a validator')) + with patch.dict(sys.modules, {'jsonschema': None}): + with self.assertRaises(converter.ConversionError) as caught: + self.catalog.validate(template) + raw = 'python3-jsonschema is required to generate templates' + self.assertEqual(str(caught.exception), raw) + self.assertEqual(i18n.diagnostic(caught.exception), 'Na generovanie šablón je potrebný python3-jsonschema') + self.assertEqual(str(caught.exception), raw) + self.assertIsNone(self.catalog.validate(template, required=False)) + read.assert_not_called() + self.assertEqual(template, {}) + for effect in self.effects: + effect.assert_not_called() + def test_real_casaos_error_preserves_the_protocol_and_localizes_the_message(self): with self.assertRaises(converter.ConversionError) as caught: casaos.parse_casaos_compose('[]') diff --git a/.github/workflows/test-i18n.yml b/.github/workflows/test-i18n.yml index 7f4dfb51..828db33a 100644 --- a/.github/workflows/test-i18n.yml +++ b/.github/workflows/test-i18n.yml @@ -71,8 +71,8 @@ jobs: run: | test -x /bin/bash test -x /usr/bin/jq - - name: Install OCI wording test dependency - run: python3 -m pip install 'PyYAML>=6,<7' + - name: Install OCI wording test dependencies + run: python3 -m pip install 'PyYAML>=6,<7' 'jsonschema==4.25.1' - name: Run offline i18n and script regressions env: PYTHONDONTWRITEBYTECODE: '1'